]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/mark.c
Merge changes from CUPS 1.4svn-r7961.
[thirdparty/cups.git] / cups / mark.c
index cbeb54ff1cfed4301461d2f6e88201bc205bb1a0..878ace885dba71d01e621950a48406a25d258e30 100644 (file)
@@ -1,25 +1,16 @@
 /*
- * "$Id: mark.c 4494 2005-02-18 02:18:11Z mike $"
+ * "$Id: mark.c 7819 2008-08-01 00:27:24Z mike $"
  *
  *   Option marking routines for the Common UNIX Printing System (CUPS).
  *
- *   Copyright 1997-2005 by Easy Software Products, all rights reserved.
+ *   Copyright 2007-2008 by Apple Inc.
+ *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  *   These coded instructions, statements, and computer programs are the
- *   property of Easy Software Products and are protected by Federal
- *   copyright law.  Distribution and use rights are outlined in the file
- *   "LICENSE.txt" which should have been included with this file.  If this
- *   file is missing or damaged please contact Easy Software Products
- *   at:
- *
- *       Attn: CUPS Licensing Information
- *       Easy Software Products
- *       44141 Airport View Drive, Suite 204
- *       Hollywood, Maryland 20636 USA
- *
- *       Voice: (301) 373-9600
- *       EMail: cups-info@cups.org
- *         WWW: http://www.cups.org
+ *   property of Apple Inc. and are protected by Federal copyright
+ *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ *   which should have been included with this file.  If this file is
+ *   file is missing or damaged, see the license at "http://www.cups.org/".
  *
  *   PostScript is a trademark of Adobe Systems, Inc.
  *
  *
  * Contents:
  *
- *   ppdConflicts()        - Check to see if there are any conflicts.
+ *   cupsMarkOptions()     - Mark command-line options in a PPD file.
  *   ppdFindChoice()       - Return a pointer to an option choice.
  *   ppdFindMarkedChoice() - Return the marked choice for the specified option.
  *   ppdFindOption()       - Return a pointer to the specified option.
- *   ppdIsMarked()         - Check to see if an option is marked...
+ *   ppdIsMarked()         - Check to see if an option is marked.
  *   ppdMarkDefaults()     - Mark all default options in the PPD file.
- *   ppdMarkOption()       - Mark an option in a PPD file.
+ *   ppdMarkOption()       - Mark an option in a PPD file and return the number
+ *                           of conflicts.
+ *   ppdFirstOption()      - Return the first option in the PPD file.
+ *   ppdNextOption()       - Return the next option in the PPD file.
+ *   _ppdParseOptions()    - Parse options from a PPD file.
+ *   debug_marked()        - Output the marked array to stdout...
  *   ppd_defaults()        - Set the defaults for this group and all sub-groups.
+ *   ppd_mark_choices()    - Mark one or more option choices from a string.
+ *   ppd_mark_option()     - Quick mark an option without checking for
+ *                           conflicts.
  */
 
 /*
  * Include necessary headers...
  */
 
-#include "ppd.h"
+#include "cups.h"
 #include "string.h"
 #include "debug.h"
 
  * Local functions...
  */
 
+#ifdef DEBUG
+static void    debug_marked(ppd_file_t *ppd, const char *title);
+#else
+#  define      debug_marked(ppd,title)
+#endif /* DEBUG */
 static void    ppd_defaults(ppd_file_t *ppd, ppd_group_t *g);
+static void    ppd_mark_choices(ppd_file_t *ppd, const char *s);
+static void    ppd_mark_option(ppd_file_t *ppd, const char *option,
+                               const char *choice);
 
 
 /*
- * 'ppdConflicts()' - Check to see if there are any conflicts.
+ * 'cupsMarkOptions()' - Mark command-line options in a PPD file.
+ *
+ * This function maps the IPP "finishings", "media", "mirror",
+ * "multiple-document-handling", "output-bin", "printer-resolution", and
+ * "sides" attributes to their corresponding PPD options and choices.
  */
 
-int                            /* O - Number of conflicts found */
-ppdConflicts(ppd_file_t *ppd)  /* I - PPD to check */
+int                                    /* O - 1 if conflicts exist, 0 otherwise */
+cupsMarkOptions(
+    ppd_file_t    *ppd,                        /* I - PPD file */
+    int           num_options,         /* I - Number of options */
+    cups_option_t *options)            /* I - Options */
 {
-  int          i, j, k,        /* Looping variables */
-               conflicts;      /* Number of conflicts */
-  ppd_const_t  *c;             /* Current constraint */
-  ppd_group_t  *g, *sg;        /* Groups */
-  ppd_option_t *o1, *o2;       /* Options */
-  ppd_choice_t *c1, *c2;       /* Choices */
+  int          i, j, k;                /* Looping vars */
+  char         *val,                   /* Pointer into value */
+               *ptr,                   /* Pointer into string */
+               s[255];                 /* Temporary string */
+  const char   *page_size;             /* PageSize option */
+  cups_option_t        *optptr;                /* Current option */
+  ppd_option_t *option;                /* PPD option */
+  ppd_attr_t   *attr;                  /* PPD attribute */
+  static const char * const duplex_options[] =
+               {                       /* Duplex option names */
+                 "Duplex",             /* Adobe */
+                 "EFDuplex",           /* EFI */
+                 "EFDuplexing",        /* EFI */
+                 "KD03Duplex",         /* Kodak */
+                 "JCLDuplex"           /* Samsung */
+               };
+  static const char * const duplex_one[] =
+               {                       /* one-sided names */
+                 "None",
+                 "False"
+               };
+  static const char * const duplex_two_long[] =
+               {                       /* two-sided-long-edge names */
+                 "DuplexNoTumble",     /* Adobe */
+                 "LongEdge",           /* EFI */
+                 "Top"                 /* EFI */
+               };
+  static const char * const duplex_two_short[] =
+               {                       /* two-sided-long-edge names */
+                 "DuplexTumble",       /* Adobe */
+                 "ShortEdge",          /* EFI */
+                 "Bottom"              /* EFI */
+               };
 
 
-  if (ppd == NULL)
+ /*
+  * Check arguments...
+  */
+
+  if (!ppd || num_options <= 0 || !options)
     return (0);
 
+  debug_marked(ppd, "Before...");
+
  /*
-  * Clear all conflicts...
+  * Mark options...
   */
 
-  conflicts = 0;
+  for (i = num_options, optptr = options; i > 0; i --, optptr ++)
+    if (!strcasecmp(optptr->name, "media"))
+    {
+     /*
+      * Loop through the option string, separating it at commas and
+      * marking each individual option as long as the corresponding
+      * PPD option (PageSize, InputSlot, etc.) is not also set.
+      *
+      * For PageSize, we also check for an empty option value since
+      * some versions of MacOS X use it to specify auto-selection
+      * of the media based solely on the size.
+      */
+
+      page_size = cupsGetOption("PageSize", num_options, options);
 
-  for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++)
-  {
-    for (j = g->num_options, o1 = g->options; j > 0; j --, o1 ++)
-      o1->conflicted = 0;
+      for (val = optptr->value; *val;)
+      {
+       /*
+        * Extract the sub-option from the string...
+       */
 
-    for (j = g->num_subgroups, sg = g->subgroups; j > 0; j --, sg ++)
-      for (k = sg->num_options, o1 = sg->options; k > 0; k --, o1 ++)
-        o1->conflicted = 0;
-  }
+        for (ptr = s; *val && *val != ',' && (ptr - s) < (sizeof(s) - 1);)
+         *ptr++ = *val++;
+       *ptr++ = '\0';
 
- /*
-  * Loop through all of the UI constraints and flag any options
-  * that conflict...
-  */
+       if (*val == ',')
+         val ++;
 
-  for (i = ppd->num_consts, c = ppd->consts; i > 0; i --, c ++)
-  {
-   /*
-    * Grab pointers to the first option...
-    */
+       /*
+        * Mark it...
+       */
 
-    o1 = ppdFindOption(ppd, c->option1);
+        if (!page_size || !page_size[0])
+         ppd_mark_option(ppd, "PageSize", s);
 
-    if (o1 == NULL)
-      continue;
-    else if (c->choice1[0] != '\0')
+        if (cupsGetOption("InputSlot", num_options, options) == NULL)
+         ppd_mark_option(ppd, "InputSlot", s);
+
+        if (cupsGetOption("MediaType", num_options, options) == NULL)
+         ppd_mark_option(ppd, "MediaType", s);
+
+        if (cupsGetOption("EFMediaType", num_options, options) == NULL)
+         ppd_mark_option(ppd, "EFMediaType", s);               /* EFI */
+
+        if (cupsGetOption("EFMediaQualityMode", num_options, options) == NULL)
+         ppd_mark_option(ppd, "EFMediaQualityMode", s);        /* EFI */
+
+       if (!strcasecmp(s, "manual") &&
+           !cupsGetOption("ManualFeed", num_options, options))
+          ppd_mark_option(ppd, "ManualFeed", "True");
+      }
+    }
+    else if (!strcasecmp(optptr->name, "sides"))
     {
-     /*
-      * This constraint maps to a specific choice.
-      */
+      for (j = 0;
+           j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0]));
+          j ++)
+        if (cupsGetOption(duplex_options[j], num_options, options))
+         break;
+
+      if (j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0])))
+      {
+       /*
+        * Don't override the PPD option with the IPP attribute...
+       */
+
+        continue;
+      }
 
-      c1 = ppdFindChoice(o1, c->choice1);
+      if (!strcasecmp(optptr->value, "one-sided"))
+      {
+       /*
+        * Mark the appropriate duplex option for one-sided output...
+       */
+
+        for (j = 0;
+            j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0]));
+            j ++)
+         if ((option = ppdFindOption(ppd, duplex_options[j])) != NULL)
+           break;
+
+       if (j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0])))
+       {
+          for (k = 0;
+              k < (int)(sizeof(duplex_one) / sizeof(duplex_one[0]));
+              k ++)
+            if (ppdFindChoice(option, duplex_one[k]))
+           {
+             ppd_mark_option(ppd, duplex_options[j], duplex_one[k]);
+             break;
+            }
+        }
+      }
+      else if (!strcasecmp(optptr->value, "two-sided-long-edge"))
+      {
+       /*
+        * Mark the appropriate duplex option for two-sided-long-edge output...
+       */
+
+        for (j = 0;
+            j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0]));
+            j ++)
+         if ((option = ppdFindOption(ppd, duplex_options[j])) != NULL)
+           break;
+
+       if (j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0])))
+       {
+          for (k = 0;
+              k < (int)(sizeof(duplex_two_long) / sizeof(duplex_two_long[0]));
+              k ++)
+            if (ppdFindChoice(option, duplex_two_long[k]))
+           {
+             ppd_mark_option(ppd, duplex_options[j], duplex_two_long[k]);
+             break;
+            }
+        }
+      }
+      else if (!strcasecmp(optptr->value, "two-sided-short-edge"))
+      {
+       /*
+        * Mark the appropriate duplex option for two-sided-short-edge output...
+       */
+
+        for (j = 0;
+            j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0]));
+            j ++)
+         if ((option = ppdFindOption(ppd, duplex_options[j])) != NULL)
+           break;
+
+       if (j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0])))
+       {
+          for (k = 0;
+              k < (int)(sizeof(duplex_two_short) / sizeof(duplex_two_short[0]));
+              k ++)
+            if (ppdFindChoice(option, duplex_two_short[k]))
+           {
+             ppd_mark_option(ppd, duplex_options[j], duplex_two_short[k]);
+             break;
+            }
+        }
+      }
     }
-    else
+    else if (!strcasecmp(optptr->name, "resolution") ||
+             !strcasecmp(optptr->name, "printer-resolution"))
+    {
+      ppd_mark_option(ppd, "Resolution", optptr->value);
+      ppd_mark_option(ppd, "SetResolution", optptr->value);
+       /* Calcomp, Linotype, QMS, Summagraphics, Tektronix, Varityper */
+      ppd_mark_option(ppd, "JCLResolution", optptr->value);
+       /* HP */
+      ppd_mark_option(ppd, "CNRes_PGP", optptr->value);
+       /* Canon */
+    }
+    else if (!strcasecmp(optptr->name, "output-bin"))
+    {
+      if (!cupsGetOption("OutputBin", num_options, options))
+        ppd_mark_option(ppd, "OutputBin", optptr->value);
+    }
+    else if (!strcasecmp(optptr->name, "multiple-document-handling"))
+    {
+      if (!cupsGetOption("Collate", num_options, options) &&
+          ppdFindOption(ppd, "Collate"))
+      {
+        if (strcasecmp(optptr->value, "separate-documents-uncollated-copies"))
+         ppd_mark_option(ppd, "Collate", "True");
+       else
+         ppd_mark_option(ppd, "Collate", "False");
+      }
+    }
+    else if (!strcasecmp(optptr->name, "finishings"))
     {
      /*
-      * This constraint applies to any choice for this option.
+      * Lookup cupsIPPFinishings attributes for each value...
       */
 
-      for (j = o1->num_choices, c1 = o1->choices; j > 0; j --, c1 ++)
-        if (c1->marked)
+      for (ptr = optptr->value; *ptr;)
+      {
+       /*
+        * Get the next finishings number...
+       */
+
+        if (!isdigit(*ptr & 255))
          break;
 
-      if (j == 0 ||
-          strcasecmp(c1->choice, "None") == 0 ||
-          strcasecmp(c1->choice, "Off") == 0 ||
-          strcasecmp(c1->choice, "False") == 0)
-        c1 = NULL;
-    }
+        if ((j = strtol(ptr, &ptr, 10)) < 3)
+         break;
 
-   /*
-    * Grab pointers to the second option...
-    */
+       /*
+        * Skip separator as needed...
+       */
 
-    o2 = ppdFindOption(ppd, c->option2);
+        if (*ptr == ',')
+         ptr ++;
 
-    if (o2 == NULL)
-      continue;
-    else if (c->choice2[0] != '\0')
-    {
-     /*
-      * This constraint maps to a specific choice.
-      */
+       /*
+        * Look it up in the PPD file...
+       */
+
+       sprintf(s, "%d", j);
+
+        if ((attr = ppdFindAttr(ppd, "cupsIPPFinishings", s)) == NULL)
+         continue;
+
+       /*
+        * Apply "*Option Choice" settings from the attribute value...
+       */
 
-      c2 = ppdFindChoice(o2, c->choice2);
+        ppd_mark_choices(ppd, attr->value);
+      }
     }
-    else
+    else if (!strcasecmp(optptr->name, "APPrinterPreset"))
     {
      /*
-      * This constraint applies to any choice for this option.
+      * Lookup APPrinterPreset value...
       */
 
-      for (j = o2->num_choices, c2 = o2->choices; j > 0; j --, c2 ++)
-        if (c2->marked)
-         break;
-
-      if (j == 0 ||
-          strcasecmp(c2->choice, "None") == 0 ||
-          strcasecmp(c2->choice, "Off") == 0 ||
-          strcasecmp(c2->choice, "False") == 0)
-        c2 = NULL;
-    }
-
-   /*
-    * If both options are marked then there is a conflict...
-    */
+      if ((attr = ppdFindAttr(ppd, "APPrinterPreset", optptr->value)) != NULL)
+      {
+       /*
+        * Apply "*Option Choice" settings from the attribute value...
+       */
 
-    if (c1 != NULL && c1->marked &&
-        c2 != NULL && c2->marked)
-    {
-      DEBUG_printf(("%s->%s conflicts with %s->%s (%s %s %s %s)\n",
-                    o1->keyword, c1->choice, o2->keyword, c2->choice,
-                   c->option1, c->choice1, c->option2, c->choice2));
-      conflicts ++;
-      o1->conflicted = 1;
-      o2->conflicted = 1;
+        ppd_mark_choices(ppd, attr->value);
+      }
     }
-  }
+    else if (!strcasecmp(optptr->name, "mirror"))
+      ppd_mark_option(ppd, "MirrorPrint", optptr->value);
+    else
+      ppd_mark_option(ppd, optptr->name, optptr->value);
 
- /*
-  * Return the number of conflicts found...
-  */
+  debug_marked(ppd, "After...");
 
-  return (conflicts);
+  return (ppdConflicts(ppd) > 0);
 }
 
 
@@ -188,19 +362,22 @@ ppdConflicts(ppd_file_t *ppd)     /* I - PPD to check */
  * 'ppdFindChoice()' - Return a pointer to an option choice.
  */
 
-ppd_choice_t *                         /* O - Choice pointer or NULL */
+ppd_choice_t *                         /* O - Choice pointer or @code NULL@ */
 ppdFindChoice(ppd_option_t *o,         /* I - Pointer to option */
               const char   *choice)    /* I - Name of choice */
 {
-  int          i;              /* Looping var */
-  ppd_choice_t *c;             /* Current choice */
+  int          i;                      /* Looping var */
+  ppd_choice_t *c;                     /* Current choice */
 
 
-  if (o == NULL || choice == NULL)
+  if (!o || !choice)
     return (NULL);
 
+  if (choice[0] == '{' || !strncasecmp(choice, "Custom.", 7))
+    choice = "Custom";
+
   for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
-    if (strcasecmp(c->choice, choice) == 0)
+    if (!strcasecmp(c->choice, choice))
       return (c);
 
   return (NULL);
@@ -211,23 +388,17 @@ ppdFindChoice(ppd_option_t *o,            /* I - Pointer to option */
  * 'ppdFindMarkedChoice()' - Return the marked choice for the specified option.
  */
 
-ppd_choice_t *                         /* O - Pointer to choice or NULL */
+ppd_choice_t *                         /* O - Pointer to choice or @code NULL@ */
 ppdFindMarkedChoice(ppd_file_t *ppd,   /* I - PPD file */
                     const char *option)        /* I - Keyword/option name */
 {
-  int          i;              /* Looping var */
-  ppd_option_t *o;             /* Pointer to option */
-  ppd_choice_t *c;             /* Pointer to choice */
+  ppd_choice_t key;                    /* Search key for choice */
 
 
-  if ((o = ppdFindOption(ppd, option)) == NULL)
+  if ((key.option = ppdFindOption(ppd, option)) == NULL)
     return (NULL);
 
-  for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
-    if (c->marked)
-      return (c);
-
-  return (NULL);
+  return ((ppd_choice_t *)cupsArrayFind(ppd->marked, &key));
 }
 
 
@@ -235,58 +406,76 @@ ppdFindMarkedChoice(ppd_file_t *ppd,      /* I - PPD file */
  * 'ppdFindOption()' - Return a pointer to the specified option.
  */
 
-ppd_option_t *                         /* O - Pointer to option or NULL */
+ppd_option_t *                         /* O - Pointer to option or @code NULL@ */
 ppdFindOption(ppd_file_t *ppd,         /* I - PPD file data */
               const char *option)      /* I - Option/Keyword name */
 {
-  int          i, j, k;        /* Looping vars */
-  ppd_option_t *o;             /* Pointer to option */
-  ppd_group_t  *g,             /* Pointer to group */
-               *sg;            /* Pointer to subgroup */
-
+ /*
+  * Range check input...
+  */
 
-  if (ppd == NULL || option == NULL)
+  if (!ppd || !option)
     return (NULL);
 
-  for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++)
+  if (ppd->options)
   {
-    for (j = g->num_options, o = g->options; j > 0; j --, o ++)
-      if (strcasecmp(o->keyword, option) == 0)
-       return (o);
-
-    for (j = g->num_subgroups, sg = g->subgroups; j > 0; j --, sg ++)
-      for (k = sg->num_options, o = sg->options; k > 0; k --, o ++)
-       if (strcasecmp(o->keyword, option) == 0)
-         return (o);
+   /*
+    * Search in the array...
+    */
+
+    ppd_option_t       key;            /* Option search key */
+
+
+    strlcpy(key.keyword, option, sizeof(key.keyword));
+
+    return ((ppd_option_t *)cupsArrayFind(ppd->options, &key));
   }
+  else
+  {
+   /*
+    * Search in each group...
+    */
 
-  return (NULL);
+    int                        i, j;           /* Looping vars */
+    ppd_group_t                *group;         /* Current group */
+    ppd_option_t       *optptr;        /* Current option */
+
+
+    for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
+      for (j = group->num_options, optptr = group->options;
+           j > 0;
+          j --, optptr ++)
+        if (!strcasecmp(optptr->keyword, option))
+         return (optptr);
+
+    return (NULL);
+  }
 }
 
 
 /*
- * 'ppdIsMarked()' - Check to see if an option is marked...
+ * 'ppdIsMarked()' - Check to see if an option is marked.
  */
 
-int                            /* O - Non-zero if option is marked */
-ppdIsMarked(ppd_file_t *ppd,   /* I - PPD file data */
-            const char *option,        /* I - Option/Keyword name */
-            const char *choice)        /* I - Choice name */
+int                                    /* O - Non-zero if option is marked */
+ppdIsMarked(ppd_file_t *ppd,           /* I - PPD file data */
+            const char *option,                /* I - Option/Keyword name */
+            const char *choice)                /* I - Choice name */
 {
-  ppd_option_t *o;             /* Option pointer */
-  ppd_choice_t *c;             /* Choice pointer */
+  ppd_choice_t key,                    /* Search key */
+               *c;                     /* Choice pointer */
 
 
-  if (ppd == NULL)
+  if (!ppd)
     return (0);
 
-  if ((o = ppdFindOption(ppd, option)) == NULL)
+  if ((key.option = ppdFindOption(ppd, option)) == NULL)
     return (0);
 
-  if ((c = ppdFindChoice(o, choice)) == NULL)
+  if ((c = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) == NULL)
     return (0);
 
-  return (c->marked);
+  return (!strcmp(c->choice, choice));
 }
 
 
@@ -295,27 +484,37 @@ ppdIsMarked(ppd_file_t *ppd,      /* I - PPD file data */
  */
 
 void
-ppdMarkDefaults(ppd_file_t *ppd)/* I - PPD file record */
+ppdMarkDefaults(ppd_file_t *ppd)       /* I - PPD file record */
 {
-  int          i;              /* Looping variables */
-  ppd_group_t  *g;             /* Current group */
+  int          i;                      /* Looping variables */
+  ppd_group_t  *g;                     /* Current group */
+  ppd_choice_t *c;                     /* Current choice */
 
 
-  if (ppd == NULL)
+  if (!ppd)
     return;
 
+ /*
+  * Clean out the marked array...
+  */
+
+  for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
+       c;
+       c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
+    cupsArrayRemove(ppd->marked, c);
+
+ /*
+  * Then repopulate it with the defaults...
+  */
+
   for (i = ppd->num_groups, g = ppd->groups; i > 0; i --, g ++)
     ppd_defaults(ppd, g);
 }
 
 
 /*
- * 'ppdMarkOption()' - Mark an option in a PPD file.
- *
- * Notes:
- *
- *   -1 is returned if the given option would conflict with any currently
- *   selected option.
+ * 'ppdMarkOption()' - Mark an option in a PPD file and return the number of
+ *                     conflicts.
  */
 
 int                                    /* O - Number of conflicts */
@@ -323,121 +522,530 @@ ppdMarkOption(ppd_file_t *ppd,          /* I - PPD file record */
               const char *option,      /* I - Keyword */
               const char *choice)      /* I - Option name */
 {
-  int          i;              /* Looping var */
-  ppd_option_t *o;             /* Option pointer */
-  ppd_choice_t *c;             /* Choice pointer */
+  DEBUG_printf(("ppdMarkOption(ppd=%p, option=\"%s\", choice=\"%s\")\n",
+               ppd, option, choice));
 
+ /*
+  * Range check input...
+  */
 
-  if (ppd == NULL)
+  if (!ppd || !option || !choice)
     return (0);
 
-  if (strcasecmp(option, "PageSize") == 0 && strncasecmp(choice, "Custom.", 7) == 0)
+ /*
+  * Mark the option...
+  */
+
+  ppd_mark_option(ppd, option, choice);
+
+ /*
+  * Return the number of conflicts...
+  */
+
+  return (ppdConflicts(ppd));
+}
+
+
+/*
+ * 'ppdFirstOption()' - Return the first option in the PPD file.
+ *
+ * Options are returned from all groups in ascending alphanumeric order.
+ *
+ * @since CUPS 1.2@
+ */
+
+ppd_option_t *                         /* O - First option or @code NULL@ */
+ppdFirstOption(ppd_file_t *ppd)                /* I - PPD file */
+{
+  if (!ppd)
+    return (NULL);
+  else
+    return ((ppd_option_t *)cupsArrayFirst(ppd->options));
+}
+
+
+/*
+ * 'ppdNextOption()' - Return the next option in the PPD file.
+ *
+ * Options are returned from all groups in ascending alphanumeric order.
+ *
+ * @since CUPS 1.2@
+ */
+
+ppd_option_t *                         /* O - Next option or @code NULL@ */
+ppdNextOption(ppd_file_t *ppd)         /* I - PPD file */
+{
+  if (!ppd)
+    return (NULL);
+  else
+    return ((ppd_option_t *)cupsArrayNext(ppd->options));
+}
+
+
+/*
+ * '_ppdParseOptions()' - Parse options from a PPD file.
+ *
+ * This function looks for strings of the form:
+ *
+ *     *option choice ... *optionN choiceN
+ *
+ * It stops when it finds a string that doesn't match this format.
+ */
+
+int                                    /* O  - Number of options */
+_ppdParseOptions(
+    const char    *s,                  /* I  - String to parse */
+    int           num_options,         /* I  - Number of options */
+    cups_option_t **options)           /* IO - Options */
+{
+  char option[PPD_MAX_NAME],           /* Current option */
+       choice[PPD_MAX_NAME],           /* Current choice */
+       *ptr;                           /* Pointer into option or choice */
+
+
+  if (!s)
+    return (num_options);
+
+ /*
+  * Read all of the "*Option Choice" pairs from the string, marking PPD
+  * options as we go...
+  */
+
+  while (*s)
   {
    /*
-    * Handle variable page sizes...
+    * Skip leading whitespace...
     */
 
-    ppdPageSize(ppd, choice);
-    choice = "Custom";
+    while (isspace(*s & 255))
+      s ++;
+
+    if (*s != '*')
+      break;
+
+   /*
+    * Get the option name...
+    */
+
+    s ++;
+    ptr = option;
+    while (*s && !isspace(*s & 255) && ptr < (option + sizeof(option) - 1))
+      *ptr++ = *s++;
+
+    if (ptr == s)
+      break;
+
+    *ptr = '\0';
+
+   /*
+    * Get the choice...
+    */
+
+    while (isspace(*s & 255))
+      s ++;
+
+    if (!*s)
+      break;
+
+    ptr = choice;
+    while (*s && !isspace(*s & 255) && ptr < (choice + sizeof(choice) - 1))
+      *ptr++ = *s++;
+
+    *ptr = '\0';
+
+   /*
+    * Add it to the options array...
+    */
+
+    num_options = cupsAddOption(option, choice, num_options, options);
+  }
+
+  return (num_options);
+}
+
+
+#ifdef DEBUG
+/*
+ * 'debug_marked()' - Output the marked array to stdout...
+ */
+
+static void
+debug_marked(ppd_file_t *ppd,          /* I - PPD file data */
+             const char *title)                /* I - Title for list */
+{
+  ppd_choice_t *c;                     /* Current choice */
+
+
+  DEBUG_printf(("cupsMarkOptions: %s\n", title));
+
+  for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
+       c;
+       c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
+    DEBUG_printf(("cupsMarkOptions: %s=%s\n", c->option->keyword, c->choice));
+}
+#endif /* DEBUG */
+
+
+/*
+ * 'ppd_defaults()' - Set the defaults for this group and all sub-groups.
+ */
+
+static void
+ppd_defaults(ppd_file_t  *ppd,         /* I - PPD file */
+             ppd_group_t *g)           /* I - Group to default */
+{
+  int          i;                      /* Looping var */
+  ppd_option_t *o;                     /* Current option */
+  ppd_group_t  *sg;                    /* Current sub-group */
+
+
+  for (i = g->num_options, o = g->options; i > 0; i --, o ++)
+    if (strcasecmp(o->keyword, "PageRegion") != 0)
+      ppdMarkOption(ppd, o->keyword, o->defchoice);
+
+  for (i = g->num_subgroups, sg = g->subgroups; i > 0; i --, sg ++)
+    ppd_defaults(ppd, sg);
+}
+
+
+/*
+ * 'ppd_mark_choices()' - Mark one or more option choices from a string.
+ */
+
+static void
+ppd_mark_choices(ppd_file_t *ppd,      /* I - PPD file */
+                 const char *s)                /* I - "*Option Choice ..." string */
+{
+  int          i,                      /* Looping var */
+               num_options;            /* Number of options */
+  cups_option_t        *options,               /* Options */
+               *option;                /* Current option */
+
+
+  if (!s)
+    return;
+
+  options     = NULL;
+  num_options = _ppdParseOptions(s, 0, &options);
+
+  for (i = num_options, option = options; i > 0; i --, option ++)
+    ppd_mark_option(ppd, option->name, option->value);
+
+  cupsFreeOptions(num_options, options);
+}
+
+
+/*
+ * 'ppd_mark_option()' - Quick mark an option without checking for conflicts.
+ */
+
+static void
+ppd_mark_option(ppd_file_t *ppd,       /* I - PPD file */
+                const char *option,    /* I - Option name */
+                const char *choice)    /* I - Choice name */
+{
+  int          i, j;                   /* Looping vars */
+  ppd_option_t *o;                     /* Option pointer */
+  ppd_choice_t *c,                     /* Choice pointer */
+               *oldc,                  /* Old choice pointer */
+               key;                    /* Search key for choice */
+  struct lconv *loc;                   /* Locale data */
+
+
+  DEBUG_printf(("ppd_mark_option(ppd=%p, option=\"%s\", choice=\"%s\")\n",
+               ppd, option, choice));
+
+ /*
+  * AP_D_InputSlot is the "default input slot" on MacOS X, and setting
+  * it clears the regular InputSlot choices...
+  */
+
+  if (!strcasecmp(option, "AP_D_InputSlot"))
+  {
+    if ((o = ppdFindOption(ppd, "InputSlot")) != NULL)
+    {
+      key.option = o;
+      if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
+      {
+        oldc->marked = 0;
+        cupsArrayRemove(ppd->marked, oldc);
+      }
+    }
   }
 
+ /*
+  * Check for custom options...
+  */
+
   if ((o = ppdFindOption(ppd, option)) == NULL)
-    return (0);
+    return;
 
-  for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
-    if (strcasecmp(c->choice, choice) == 0)
-      break;
+  loc = localeconv();
+
+  if (!strncasecmp(choice, "Custom.", 7))
+  {
+   /*
+    * Handle a custom option...
+    */
+
+    if ((c = ppdFindChoice(o, "Custom")) == NULL)
+      return;
+
+    if (!strcasecmp(option, "PageSize"))
+    {
+     /*
+      * Handle custom page sizes...
+      */
+
+      ppdPageSize(ppd, choice);
+    }
+    else
+    {
+     /*
+      * Handle other custom options...
+      */
+
+      ppd_coption_t    *coption;       /* Custom option */
+      ppd_cparam_t     *cparam;        /* Custom parameter */
+      char             *units;         /* Custom points units */
+
+
+      if ((coption = ppdFindCustomOption(ppd, option)) != NULL)
+      {
+        if ((cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params)) == NULL)
+         return;
+
+        switch (cparam->type)
+       {
+         case PPD_CUSTOM_CURVE :
+         case PPD_CUSTOM_INVCURVE :
+         case PPD_CUSTOM_REAL :
+             cparam->current.custom_real = (float)_cupsStrScand(choice + 7,
+                                                                NULL, loc);
+             break;
+
+         case PPD_CUSTOM_POINTS :
+             cparam->current.custom_points = (float)_cupsStrScand(choice + 7,
+                                                                  &units,
+                                                                  loc);
+
+              if (units)
+             {
+               if (!strcasecmp(units, "cm"))
+                 cparam->current.custom_points *= 72.0f / 2.54f;             
+               else if (!strcasecmp(units, "mm"))
+                 cparam->current.custom_points *= 72.0f / 25.4f;             
+               else if (!strcasecmp(units, "m"))
+                 cparam->current.custom_points *= 72.0f / 0.0254f;           
+               else if (!strcasecmp(units, "in"))
+                 cparam->current.custom_points *= 72.0f;             
+               else if (!strcasecmp(units, "ft"))
+                 cparam->current.custom_points *= 12.0f * 72.0f;             
+              }
+             break;
+
+         case PPD_CUSTOM_INT :
+             cparam->current.custom_int = atoi(choice + 7);
+             break;
+
+         case PPD_CUSTOM_PASSCODE :
+         case PPD_CUSTOM_PASSWORD :
+         case PPD_CUSTOM_STRING :
+             if (cparam->current.custom_string)
+               _cupsStrFree(cparam->current.custom_string);
+
+             cparam->current.custom_string = _cupsStrAlloc(choice + 7);
+             break;
+       }
+      }
+    }
 
-  if (i)
+   /*
+    * Make sure that we keep the option marked below...
+    */
+
+    choice = "Custom";
+  }
+  else if (choice[0] == '{')
   {
    /*
-    * Option found; mark it and then handle unmarking any other options.
+    * Handle multi-value custom options...
     */
 
-    c->marked = 1;
+    ppd_coption_t      *coption;       /* Custom option */
+    ppd_cparam_t       *cparam;        /* Custom parameter */
+    char               *units;         /* Custom points units */
+    int                        num_vals;       /* Number of values */
+    cups_option_t      *vals,          /* Values */
+                       *val;           /* Value */
+
+
+    if ((c = ppdFindChoice(o, "Custom")) == NULL)
+      return;
+
+    if ((coption = ppdFindCustomOption(ppd, option)) != NULL)
+    {
+      num_vals = cupsParseOptions(choice, 0, &vals);
+
+      for (i = 0, val = vals; i < num_vals; i ++, val ++)
+      {
+        if ((cparam = ppdFindCustomParam(coption, val->name)) == NULL)
+         continue;
+
+       switch (cparam->type)
+       {
+         case PPD_CUSTOM_CURVE :
+         case PPD_CUSTOM_INVCURVE :
+         case PPD_CUSTOM_REAL :
+             cparam->current.custom_real = (float)_cupsStrScand(val->value,
+                                                                NULL, loc);
+             break;
+
+         case PPD_CUSTOM_POINTS :
+             cparam->current.custom_points = (float)_cupsStrScand(val->value,
+                                                                  &units,
+                                                                  loc);
+
+             if (units)
+             {
+               if (!strcasecmp(units, "cm"))
+                 cparam->current.custom_points *= 72.0f / 2.54f;
+               else if (!strcasecmp(units, "mm"))
+                 cparam->current.custom_points *= 72.0f / 25.4f;
+               else if (!strcasecmp(units, "m"))
+                 cparam->current.custom_points *= 72.0f / 0.0254f;
+               else if (!strcasecmp(units, "in"))
+                 cparam->current.custom_points *= 72.0f;
+               else if (!strcasecmp(units, "ft"))
+                 cparam->current.custom_points *= 12.0f * 72.0f;
+             }
+             break;
+
+         case PPD_CUSTOM_INT :
+             cparam->current.custom_int = atoi(val->value);
+             break;
+
+         case PPD_CUSTOM_PASSCODE :
+         case PPD_CUSTOM_PASSWORD :
+         case PPD_CUSTOM_STRING :
+             if (cparam->current.custom_string)
+               _cupsStrFree(cparam->current.custom_string);
+
+             cparam->current.custom_string = _cupsStrAlloc(val->value);
+             break;
+       }
+      }
+
+      cupsFreeOptions(num_vals, vals);
+    }
+  }
+  else
+  {
+    for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
+      if (!strcasecmp(c->choice, choice))
+        break;
+
+    if (!i)
+      return;
+  }
+
+ /*
+  * Option found; mark it and then handle unmarking any other options.
+  */
+
+  if (o->ui != PPD_UI_PICKMANY)
+  {
+   /*
+    * Unmark all other choices...
+    */
 
-    if (o->ui != PPD_UI_PICKMANY)
-      for (i = o->num_choices, c = o->choices; i > 0; i --, c ++)
-       if (strcasecmp(c->choice, choice) != 0)
-          c->marked = 0;
+    if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, c)) != NULL)
+    {
+      oldc->marked = 0;
+      cupsArrayRemove(ppd->marked, oldc);
+    }
 
-    if (strcasecmp(option, "PageSize") == 0 || strcasecmp(option, "PageRegion") == 0)
+    if (!strcasecmp(option, "PageSize") || !strcasecmp(option, "PageRegion"))
     {
      /*
       * Mark current page size...
       */
 
-      for (i = 0; i < ppd->num_sizes; i ++)
-       ppd->sizes[i].marked = strcasecmp(ppd->sizes[i].name, choice) == 0;
+      for (j = 0; j < ppd->num_sizes; j ++)
+       ppd->sizes[j].marked = !strcasecmp(ppd->sizes[j].name,
+                                          choice);
 
      /*
-      * Unmark the current PageSize or PageRegion setting, as appropriate...
+      * Unmark the current PageSize or PageRegion setting, as
+      * appropriate...
       */
 
-      if (strcasecmp(option, "PageSize") == 0)
+      if (!strcasecmp(option, "PageSize"))
       {
        if ((o = ppdFindOption(ppd, "PageRegion")) != NULL)
-         for (i = 0; i < o->num_choices; i ++)
-            o->choices[i].marked = 0;
+        {
+          key.option = o;
+          if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
+          {
+            oldc->marked = 0;
+            cupsArrayRemove(ppd->marked, oldc);
+          }
+        }
       }
       else
       {
        if ((o = ppdFindOption(ppd, "PageSize")) != NULL)
-         for (i = 0; i < o->num_choices; i ++)
-            o->choices[i].marked = 0;
+        {
+          key.option = o;
+          if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
+          {
+            oldc->marked = 0;
+            cupsArrayRemove(ppd->marked, oldc);
+          }
+        }
       }
     }
-    else if (strcasecmp(option, "InputSlot") == 0)
+    else if (!strcasecmp(option, "InputSlot"))
     {
      /*
       * Unmark ManualFeed option...
       */
 
       if ((o = ppdFindOption(ppd, "ManualFeed")) != NULL)
-       for (i = 0; i < o->num_choices; i ++)
-          o->choices[i].marked = 0;
+      {
+        key.option = o;
+        if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
+        {
+          oldc->marked = 0;
+          cupsArrayRemove(ppd->marked, oldc);
+        }
+      }
     }
-    else if (strcasecmp(option, "ManualFeed") == 0)
+    else if (!strcasecmp(option, "ManualFeed") &&
+            !strcasecmp(choice, "True"))
     {
      /*
       * Unmark InputSlot option...
       */
 
       if ((o = ppdFindOption(ppd, "InputSlot")) != NULL)
-       for (i = 0; i < o->num_choices; i ++)
-          o->choices[i].marked = 0;
+      {
+        key.option = o;
+        if ((oldc = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL)
+        {
+          oldc->marked = 0;
+          cupsArrayRemove(ppd->marked, oldc);
+        }
+      }
     }
   }
 
-  return (ppdConflicts(ppd));
-}
-
-
-/*
- * 'ppd_defaults()' - Set the defaults for this group and all sub-groups.
- */
-
-static void
-ppd_defaults(ppd_file_t  *ppd, /* I - PPD file */
-             ppd_group_t *g)   /* I - Group to default */
-{
-  int          i;              /* Looping var */
-  ppd_option_t *o;             /* Current option */
-  ppd_group_t  *sg;            /* Current sub-group */
-
-
-  if (g == NULL)
-    return;
-
-  for (i = g->num_options, o = g->options; i > 0; i --, o ++)
-    if (strcasecmp(o->keyword, "PageRegion") != 0)
-      ppdMarkOption(ppd, o->keyword, o->defchoice);
+  c->marked = 1;
 
-  for (i = g->num_subgroups, sg = g->subgroups; i > 0; i --, sg ++)
-    ppd_defaults(ppd, sg);
+  cupsArrayAdd(ppd->marked, c);
 }
 
 
 /*
- * End of "$Id: mark.c 4494 2005-02-18 02:18:11Z mike $".
+ * End of "$Id: mark.c 7819 2008-08-01 00:27:24Z mike $".
  */