]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/mark.c
Merge changes from CUPS 1.5svn-r9041.
[thirdparty/cups.git] / cups / mark.c
index 0dbb0b8d1859c97c048896fe02af768b120366a9..a9b5124e34a7809d4ad4cb8caeac024856ab6814 100644 (file)
@@ -1,9 +1,9 @@
 /*
- * "$Id: mark.c 7278 2008-01-31 01:23:09Z mike $"
+ * "$Id: mark.c 8210 2009-01-09 02:30:26Z mike $"
  *
- *   Option marking routines for the Common UNIX Printing System (CUPS).
+ *   Option marking routines for CUPS.
  *
- *   Copyright 2007-2008 by Apple Inc.
+ *   Copyright 2007-2010 by Apple Inc.
  *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  *   These coded instructions, statements, and computer programs are the
  * Contents:
  *
  *   cupsMarkOptions()     - Mark command-line options in a PPD file.
- *   ppdConflicts()        - Check to see if there are any conflicts among the
- *                           marked option choices.
  *   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.
  *   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.
- *   debug_marked()        - Output the marked array to stdout...
+ *   _ppdParseOptions()    - Parse options from a PPD file.
+ *   ppd_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()     - Quickly mark an option without checking for
+ *                           conflicts.
  */
 
 /*
@@ -41,6 +43,7 @@
 #include "cups.h"
 #include "string.h"
 #include "debug.h"
+#include "pwg-private.h"
 
 
 /*
  */
 
 #ifdef DEBUG
-static void    debug_marked(ppd_file_t *ppd, const char *title);
+static void    ppd_debug_marked(ppd_file_t *ppd, const char *title);
 #else
-#  define debug_marked(ppd,title)
+#  define      ppd_debug_marked(ppd,title)
 #endif /* DEBUG */
 static void    ppd_defaults(ppd_file_t *ppd, ppd_group_t *g);
-static int     ppd_mark_choices(ppd_file_t *ppd, const char *options);
+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);
 
 
 /*
@@ -64,18 +69,19 @@ static int  ppd_mark_choices(ppd_file_t *ppd, const char *options);
  * "sides" attributes to their corresponding PPD options and choices.
  */
 
-int                                    /* O - 1 if conflicting */
+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 vars */
-  int          conflict;               /* Option conflicts */
-  char         *val,                   /* Pointer into value */
-               *ptr,                   /* Pointer into string */
+  char         *ptr,                   /* Pointer into string */
                s[255];                 /* Temporary string */
-  const char   *page_size;             /* PageSize option */
+  const char   *val,                   /* Pointer into value */
+               *media,                 /* media option */
+               *page_size,             /* PageSize option */
+               *ppd_keyword;           /* PPD keyword */
   cups_option_t        *optptr;                /* Current option */
   ppd_option_t *option;                /* PPD option */
   ppd_attr_t   *attr;                  /* PPD attribute */
@@ -113,76 +119,79 @@ cupsMarkOptions(
   if (!ppd || num_options <= 0 || !options)
     return (0);
 
-  debug_marked(ppd, "Before...");
+  ppd_debug_marked(ppd, "Before...");
 
  /*
-  * Mark options...
+  * Do special handling for media and PageSize...
   */
 
-  conflict  = 0;
+  media     = cupsGetOption("media", num_options, options);
+  page_size = cupsGetOption("PageSize", num_options, options);
 
-  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.
-      */
+  if (media)
+  {
+   /*
+    * Load PWG mapping data as needed...
+    */
 
-      page_size = cupsGetOption("PageSize", num_options, options);
+    if (!ppd->pwg)
+      ppd->pwg = _pwgCreateWithPPD(ppd);
 
-      for (val = optptr->value; *val;)
-      {
-       /*
-        * Extract the sub-option from the string...
-       */
+   /*
+    * 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.
+    */
 
-        for (ptr = s; *val && *val != ',' && (ptr - s) < (sizeof(s) - 1);)
-         *ptr++ = *val++;
-       *ptr++ = '\0';
+    for (val = media; *val;)
+    {
+     /*
+      * Extract the sub-option from the string...
+      */
 
-       if (*val == ',')
-         val ++;
+      for (ptr = s; *val && *val != ',' && (ptr - s) < (sizeof(s) - 1);)
+       *ptr++ = *val++;
+      *ptr++ = '\0';
 
-       /*
-        * Mark it...
-       */
+      if (*val == ',')
+       val ++;
 
-        if (!page_size || !page_size[0])
-         if (ppdMarkOption(ppd, "PageSize", s))
-            conflict = 1;
+     /*
+      * Mark it...
+      */
 
-        if (cupsGetOption("InputSlot", num_options, options) == NULL)
-         if (ppdMarkOption(ppd, "InputSlot", s))
-            conflict = 1;
+      if ((!page_size || !page_size[0]) &&
+         (ppd_keyword = _pwgGetPageSize((_pwg_t *)ppd->pwg, NULL, s,
+                                        NULL)) != NULL)
+       ppd_mark_option(ppd, "PageSize", ppd_keyword);
 
-        if (cupsGetOption("MediaType", num_options, options) == NULL)
-         if (ppdMarkOption(ppd, "MediaType", s))
-            conflict = 1;
+      if (!cupsGetOption("InputSlot", num_options, options) &&
+         (ppd_keyword = _pwgGetInputSlot((_pwg_t *)ppd->pwg, NULL, s)) != NULL)
+       ppd_mark_option(ppd, "InputSlot", ppd_keyword);
 
-        if (cupsGetOption("EFMediaType", num_options, options) == NULL)
-         if (ppdMarkOption(ppd, "EFMediaType", s))             /* EFI */
-            conflict = 1;
+      if (!cupsGetOption("MediaType", num_options, options) &&
+         (ppd_keyword = _pwgGetMediaType((_pwg_t *)ppd->pwg, NULL, s)) != NULL)
+       ppd_mark_option(ppd, "MediaType", ppd_keyword);
+    }
+  }
 
-        if (cupsGetOption("EFMediaQualityMode", num_options, options) == NULL)
-         if (ppdMarkOption(ppd, "EFMediaQualityMode", s))      /* EFI */
-            conflict = 1;
+ /*
+  * Mark other options...
+  */
 
-       if (strcasecmp(s, "manual") == 0 &&
-           cupsGetOption("ManualFeed", num_options, options) == NULL)
-          if (ppdMarkOption(ppd, "ManualFeed", "True"))
-           conflict = 1;
-      }
-    }
+  for (i = num_options, optptr = options; i > 0; i --, optptr ++)
+    if (!strcasecmp(optptr->name, "media"))
+      continue;
     else if (!strcasecmp(optptr->name, "sides"))
     {
-      for (j = 0; j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0])); j ++)
-        if (cupsGetOption(duplex_options[j], num_options, options) != NULL)
+      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])))
@@ -200,18 +209,20 @@ cupsMarkOptions(
         * Mark the appropriate duplex option for one-sided output...
        */
 
-        for (j = 0; j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0])); j ++)
+        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 ++)
+          for (k = 0;
+              k < (int)(sizeof(duplex_one) / sizeof(duplex_one[0]));
+              k ++)
             if (ppdFindChoice(option, duplex_one[k]))
            {
-             if (ppdMarkOption(ppd, duplex_options[j], duplex_one[k]))
-               conflict = 1;
-
+             ppd_mark_option(ppd, duplex_options[j], duplex_one[k]);
              break;
             }
         }
@@ -222,18 +233,20 @@ cupsMarkOptions(
         * Mark the appropriate duplex option for two-sided-long-edge output...
        */
 
-        for (j = 0; j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0])); j ++)
+        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 ++)
+          for (k = 0;
+              k < (int)(sizeof(duplex_two_long) / sizeof(duplex_two_long[0]));
+              k ++)
             if (ppdFindChoice(option, duplex_two_long[k]))
            {
-             if (ppdMarkOption(ppd, duplex_options[j], duplex_two_long[k]))
-               conflict = 1;
-
+             ppd_mark_option(ppd, duplex_options[j], duplex_two_long[k]);
              break;
             }
         }
@@ -244,18 +257,20 @@ cupsMarkOptions(
         * Mark the appropriate duplex option for two-sided-short-edge output...
        */
 
-        for (j = 0; j < (int)(sizeof(duplex_options) / sizeof(duplex_options[0])); j ++)
+        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 ++)
+          for (k = 0;
+              k < (int)(sizeof(duplex_two_short) / sizeof(duplex_two_short[0]));
+              k ++)
             if (ppdFindChoice(option, duplex_two_short[k]))
            {
-             if (ppdMarkOption(ppd, duplex_options[j], duplex_two_short[k]))
-               conflict = 1;
-
+             ppd_mark_option(ppd, duplex_options[j], duplex_two_short[k]);
              break;
             }
         }
@@ -264,21 +279,18 @@ cupsMarkOptions(
     else if (!strcasecmp(optptr->name, "resolution") ||
              !strcasecmp(optptr->name, "printer-resolution"))
     {
-      if (ppdMarkOption(ppd, "Resolution", optptr->value))
-        conflict = 1;
-      if (ppdMarkOption(ppd, "SetResolution", optptr->value))
+      ppd_mark_option(ppd, "Resolution", optptr->value);
+      ppd_mark_option(ppd, "SetResolution", optptr->value);
        /* Calcomp, Linotype, QMS, Summagraphics, Tektronix, Varityper */
-        conflict = 1;
-      if (ppdMarkOption(ppd, "JCLResolution", optptr->value))  /* HP */
-        conflict = 1;
-      if (ppdMarkOption(ppd, "CNRes_PGP", optptr->value))      /* Canon */
-        conflict = 1;
+      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))
-        if (ppdMarkOption(ppd, "OutputBin", optptr->value))
-          conflict = 1;
+        ppd_mark_option(ppd, "OutputBin", optptr->value);
     }
     else if (!strcasecmp(optptr->name, "multiple-document-handling"))
     {
@@ -286,15 +298,9 @@ cupsMarkOptions(
           ppdFindOption(ppd, "Collate"))
       {
         if (strcasecmp(optptr->value, "separate-documents-uncollated-copies"))
-       {
-         if (ppdMarkOption(ppd, "Collate", "True"))
-            conflict = 1;
-        }
+         ppd_mark_option(ppd, "Collate", "True");
        else
-       {
-         if (ppdMarkOption(ppd, "Collate", "False"))
-            conflict = 1;
-        }
+         ppd_mark_option(ppd, "Collate", "False");
       }
     }
     else if (!strcasecmp(optptr->name, "finishings"))
@@ -335,163 +341,86 @@ cupsMarkOptions(
         * Apply "*Option Choice" settings from the attribute value...
        */
 
-        if (ppd_mark_choices(ppd, attr->value))
-         conflict = 1;
+        ppd_mark_choices(ppd, attr->value);
       }
     }
-    else if (!strcasecmp(optptr->name, "mirror"))
-    {
-      if (ppdMarkOption(ppd, "MirrorPrint", optptr->value))
-       conflict = 1;
-    }
-    else if (ppdMarkOption(ppd, optptr->name, optptr->value))
-      conflict = 1;
-
-  debug_marked(ppd, "After...");
-
-  return (conflict);
-}
-
-
-/*
- * 'ppdConflicts()' - Check to see if there are any conflicts among the
- *                    marked option choices.
- *
- * The returned value is the same as returned by @link ppdMarkOption@.
- */
-
-int                                    /* O - Number of conflicts found */
-ppdConflicts(ppd_file_t *ppd)          /* I - PPD to check */
-{
-  int          i,                      /* Looping variable */
-               conflicts;              /* Number of conflicts */
-  ppd_const_t  *c;                     /* Current constraint */
-  ppd_option_t *o1, *o2;               /* Options */
-  ppd_choice_t *c1, *c2;               /* Choices */
-  ppd_choice_t key;                    /* Search key */
-
-
-  if (!ppd)
-    return (0);
-
- /*
-  * Clear all conflicts...
-  */
-
-  conflicts = 0;
-
-  for (o1 = ppdFirstOption(ppd); o1; o1 = ppdNextOption(ppd))
-    o1->conflicted = 0;
-
-  cupsArraySave(ppd->marked);
-
- /*
-  * Loop through all of the UI constraints and flag any options
-  * that conflict...
-  */
-
-  for (i = ppd->num_consts, c = ppd->consts, o1 = o2 = NULL, c1 = c2 = NULL;
-       i > 0;
-       i --, c ++)
-  {
-   /*
-    * Grab pointers to the first option...
-    */
-
-    if (!o1 || strcmp(c->option1, o1->keyword))
-    {
-      o1 = ppdFindOption(ppd, c->option1);
-      c1 = NULL;
-    }
-
-    if (!o1)
-      continue;
-    else if (c->choice1[0] && (!c1 || strcmp(c->choice1, c1->choice)))
-    {
-     /*
-      * This constraint maps to a specific choice.
-      */
-
-      key.option = o1;
-
-      if ((c1 = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL &&
-          (!c1->marked || strcmp(c->choice1, c1->choice)))
-        c1 = NULL;
-    }
-    else if (!c1)
+    else if (!strcasecmp(optptr->name, "print-quality"))
     {
-     /*
-      * This constraint applies to any choice for this option.
-      */
+      ppd_option_t     *output_mode = ppdFindOption(ppd, "OutputMode");
+                                       /* OutputMode option */
 
-      key.option = o1;
-
-      if ((c1 = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL &&
-          (!strcasecmp(c1->choice, "None") || !strcasecmp(c1->choice, "Off") ||
-           !strcasecmp(c1->choice, "False")))
-        c1 = NULL;
-    }
+      if (!strcmp(optptr->value, "3"))
+      {
+       /*
+        * Draft quality...
+       */
 
-   /*
-    * Grab pointers to the second option...
-    */
+       if (ppdFindChoice(output_mode, "Draft"))
+         ppd_mark_option(ppd, "OutputMode", "Draft");
+       else if (ppdFindChoice(output_mode, "Fast"))
+         ppd_mark_option(ppd, "OutputMode", "Fast");
 
-    if (!o2 || strcmp(c->option2, o2->keyword))
-    {
-      o2 = ppdFindOption(ppd, c->option2);
-      c2 = NULL;
-    }
+        if ((attr = ppdFindAttr(ppd, "APPrinterPreset",
+                               "DraftGray_with_Paper_Auto-Detect")) != NULL)
+          ppd_mark_choices(ppd, attr->value);
+      }
+      else if (!strcmp(optptr->value, "4"))
+      {
+       /*
+        * Normal quality...
+       */
 
-    if (!o2)
-      continue;
-    else if (c->choice2[0] && (!c2 || strcmp(c->choice2, c2->choice)))
-    {
-     /*
-      * This constraint maps to a specific choice.
-      */
+       if (ppdFindChoice(output_mode, "Normal"))
+         ppd_mark_option(ppd, "OutputMode", "Normal");
+       else if (ppdFindChoice(output_mode, "Good"))
+         ppd_mark_option(ppd, "OutputMode", "Good");
+
+        if ((attr = ppdFindAttr(ppd, "APPrinterPreset",
+                               "Color_with_Paper_Auto-Detect")) != NULL)
+          ppd_mark_choices(ppd, attr->value);
+        else if ((attr = ppdFindAttr(ppd, "APPrinterPreset",
+                               "Gray_with_Paper_Auto-Detect")) != NULL)
+          ppd_mark_choices(ppd, attr->value);
+      }
+      else if (!strcmp(optptr->value, "5"))
+      {
+       /*
+        * High/best/photo quality...
+       */
 
-      key.option = o2;
+       if (ppdFindChoice(output_mode, "Best"))
+         ppd_mark_option(ppd, "OutputMode", "Best");
+       else if (ppdFindChoice(output_mode, "High"))
+         ppd_mark_option(ppd, "OutputMode", "High");
 
-      if ((c2 = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL &&
-          (!c2->marked || strcmp(c->choice2, c2->choice)))
-        c2 = NULL;
+        if ((attr = ppdFindAttr(ppd, "APPrinterPreset",
+                               "Photo_on_Photo_Paper")) != NULL)
+          ppd_mark_choices(ppd, attr->value);
+      }
     }
-    else if (!c2)
+    else if (!strcasecmp(optptr->name, "APPrinterPreset"))
     {
      /*
-      * This constraint applies to any choice for this option.
+      * Lookup APPrinterPreset value...
       */
 
-      key.option = o2;
-
-      if ((c2 = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key)) != NULL &&
-          (!strcasecmp(c2->choice, "None") || !strcasecmp(c2->choice, "Off") ||
-           !strcasecmp(c2->choice, "False")))
-        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 && c1->marked && c2 && 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);
 
-  cupsArrayRestore(ppd->marked);
+  ppd_debug_marked(ppd, "After...");
 
- /*
-  * Return the number of conflicts found...
-  */
-
-  return (conflicts);
+  return (ppdConflicts(ppd) > 0);
 }
 
 
@@ -507,11 +436,14 @@ ppdFindChoice(ppd_option_t *o,            /* I - Pointer to option */
   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);
@@ -526,13 +458,24 @@ 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 */
 {
-  ppd_choice_t key;                    /* Search key for choice */
+  ppd_choice_t key,                    /* Search key for choice */
+               *marked;                /* Marked choice */
+
 
+  DEBUG_printf(("2ppdFindMarkedChoice(ppd=%p, option=\"%s\")", ppd, option));
 
   if ((key.option = ppdFindOption(ppd, option)) == NULL)
+  {
+    DEBUG_puts("3ppdFindMarkedChoice: Option not found, returning NULL");
     return (NULL);
+  }
 
-  return ((ppd_choice_t *)cupsArrayFind(ppd->marked, &key));
+  marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key);
+
+  DEBUG_printf(("3ppdFindMarkedChoice: Returning %p(%s)...", marked,
+                marked ? marked->choice : "NULL"));
+
+  return (marked);
 }
 
 
@@ -647,13 +590,236 @@ ppdMarkDefaults(ppd_file_t *ppd) /* I - PPD file record */
 
 
 /*
- * 'ppdMarkOption()' - Mark an option in a PPD file.
+ * 'ppdMarkOption()' - Mark an option in a PPD file and return the number of
+ *                     conflicts.
  */
 
 int                                    /* O - Number of conflicts */
 ppdMarkOption(ppd_file_t *ppd,         /* I - PPD file record */
               const char *option,      /* I - Keyword */
               const char *choice)      /* I - Option name */
+{
+  DEBUG_printf(("ppdMarkOption(ppd=%p, option=\"%s\", choice=\"%s\")",
+               ppd, option, choice));
+
+ /*
+  * Range check input...
+  */
+
+  if (!ppd || !option || !choice)
+    return (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/Mac OS X 10.5@
+ */
+
+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/Mac OS X 10.5@
+ */
+
+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)
+  {
+   /*
+    * Skip leading whitespace...
+    */
+
+    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
+/*
+ * 'ppd_debug_marked()' - Output the marked array to stdout...
+ */
+
+static void
+ppd_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(("2cupsMarkOptions: %s", title));
+
+  for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
+       c;
+       c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
+    DEBUG_printf(("2cupsMarkOptions: %s=%s", 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 */
@@ -663,16 +829,9 @@ ppdMarkOption(ppd_file_t *ppd,             /* I - PPD file record */
   struct lconv *loc;                   /* Locale data */
 
 
-  DEBUG_printf(("ppdMarkOption(ppd=%p, option=\"%s\", choice=\"%s\")\n",
+  DEBUG_printf(("7ppd_mark_option(ppd=%p, option=\"%s\", choice=\"%s\")",
                ppd, option, choice));
 
- /*
-  * Range check input...
-  */
-
-  if (!ppd || !option || !choice)
-    return (0);
-
  /*
   * AP_D_InputSlot is the "default input slot" on MacOS X, and setting
   * it clears the regular InputSlot choices...
@@ -680,6 +839,8 @@ ppdMarkOption(ppd_file_t *ppd,              /* I - PPD file record */
 
   if (!strcasecmp(option, "AP_D_InputSlot"))
   {
+    cupsArraySave(ppd->options);
+
     if ((o = ppdFindOption(ppd, "InputSlot")) != NULL)
     {
       key.option = o;
@@ -689,14 +850,22 @@ ppdMarkOption(ppd_file_t *ppd,            /* I - PPD file record */
         cupsArrayRemove(ppd->marked, oldc);
       }
     }
+
+    cupsArrayRestore(ppd->options);
   }
 
  /*
   * Check for custom options...
   */
 
-  if ((o = ppdFindOption(ppd, option)) == NULL)
-    return (0);
+  cupsArraySave(ppd->options);
+
+  o = ppdFindOption(ppd, option);
+
+  cupsArrayRestore(ppd->options);
+
+  if (!o)
+    return;
 
   loc = localeconv();
 
@@ -707,7 +876,7 @@ ppdMarkOption(ppd_file_t *ppd,              /* I - PPD file record */
     */
 
     if ((c = ppdFindChoice(o, "Custom")) == NULL)
-      return (0);
+      return;
 
     if (!strcasecmp(option, "PageSize"))
     {
@@ -731,7 +900,7 @@ ppdMarkOption(ppd_file_t *ppd,              /* I - PPD file record */
       if ((coption = ppdFindCustomOption(ppd, option)) != NULL)
       {
         if ((cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params)) == NULL)
-         return (0);
+         return;
 
         switch (cparam->type)
        {
@@ -750,15 +919,15 @@ ppdMarkOption(ppd_file_t *ppd,            /* I - PPD file record */
               if (units)
              {
                if (!strcasecmp(units, "cm"))
-                 cparam->current.custom_points *= 72.0f / 2.54f;             
+                 cparam->current.custom_points *= 72.0f / 2.54f;
                else if (!strcasecmp(units, "mm"))
-                 cparam->current.custom_points *= 72.0f / 25.4f;             
+                 cparam->current.custom_points *= 72.0f / 25.4f;
                else if (!strcasecmp(units, "m"))
-                 cparam->current.custom_points *= 72.0f / 0.0254f;           
+                 cparam->current.custom_points *= 72.0f / 0.0254f;
                else if (!strcasecmp(units, "in"))
-                 cparam->current.custom_points *= 72.0f;             
+                 cparam->current.custom_points *= 72.0f;
                else if (!strcasecmp(units, "ft"))
-                 cparam->current.custom_points *= 12.0f * 72.0f;             
+                 cparam->current.custom_points *= 12.0f * 72.0f;
               }
              break;
 
@@ -799,11 +968,11 @@ ppdMarkOption(ppd_file_t *ppd,            /* I - PPD file record */
 
 
     if ((c = ppdFindChoice(o, "Custom")) == NULL)
-      return (0);
+      return;
 
     if ((coption = ppdFindCustomOption(ppd, option)) != NULL)
     {
-      num_vals = cupsParseOptions(choice + 1, 0, &vals);
+      num_vals = cupsParseOptions(choice, 0, &vals);
 
       for (i = 0, val = vals; i < num_vals; i ++, val ++)
       {
@@ -849,7 +1018,7 @@ ppdMarkOption(ppd_file_t *ppd,             /* I - PPD file record */
              if (cparam->current.custom_string)
                _cupsStrFree(cparam->current.custom_string);
 
-             cparam->current.custom_string = _cupsStrAlloc(val->value);
+             cparam->current.custom_string = _cupsStrRetain(val->value);
              break;
        }
       }
@@ -864,7 +1033,7 @@ ppdMarkOption(ppd_file_t *ppd,             /* I - PPD file record */
         break;
 
     if (!i)
-      return (0);
+      return;
   }
 
  /*
@@ -898,6 +1067,8 @@ ppdMarkOption(ppd_file_t *ppd,             /* I - PPD file record */
       * appropriate...
       */
 
+      cupsArraySave(ppd->options);
+
       if (!strcasecmp(option, "PageSize"))
       {
        if ((o = ppdFindOption(ppd, "PageRegion")) != NULL)
@@ -922,6 +1093,8 @@ ppdMarkOption(ppd_file_t *ppd,             /* I - PPD file record */
           }
         }
       }
+
+      cupsArrayRestore(ppd->options);
     }
     else if (!strcasecmp(option, "InputSlot"))
     {
@@ -929,6 +1102,8 @@ ppdMarkOption(ppd_file_t *ppd,             /* I - PPD file record */
       * Unmark ManualFeed option...
       */
 
+      cupsArraySave(ppd->options);
+
       if ((o = ppdFindOption(ppd, "ManualFeed")) != NULL)
       {
         key.option = o;
@@ -938,6 +1113,8 @@ ppdMarkOption(ppd_file_t *ppd,             /* I - PPD file record */
           cupsArrayRemove(ppd->marked, oldc);
         }
       }
+
+      cupsArrayRestore(ppd->options);
     }
     else if (!strcasecmp(option, "ManualFeed") &&
             !strcasecmp(choice, "True"))
@@ -946,6 +1123,8 @@ ppdMarkOption(ppd_file_t *ppd,             /* I - PPD file record */
       * Unmark InputSlot option...
       */
 
+      cupsArraySave(ppd->options);
+
       if ((o = ppdFindOption(ppd, "InputSlot")) != NULL)
       {
         key.option = o;
@@ -955,183 +1134,118 @@ ppdMarkOption(ppd_file_t *ppd,         /* I - PPD file record */
           cupsArrayRemove(ppd->marked, oldc);
         }
       }
+
+      cupsArrayRestore(ppd->options);
     }
   }
 
   c->marked = 1;
 
   cupsArrayAdd(ppd->marked, c);
-
- /*
-  * 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.
+ * 'ppd_mark_size()' - Quickly mark a page size without checking for conflicts.
  *
- * @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));
-}
-
-
-#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 */
-
-
-  printf("cupsMarkOptions: %s\n", title);
-
-  for (c = (ppd_choice_t *)cupsArrayFirst(ppd->marked);
-       c;
-       c = (ppd_choice_t *)cupsArrayNext(ppd->marked))
-    printf("cupsMarkOptions: %s=%s\n", c->option->keyword, c->choice);
-}
-#endif /* DEBUG */
-
-
-/*
- * 'ppd_defaults()' - Set the defaults for this group and all sub-groups.
+ * This function is also responsible for mapping PWG/ISO/IPP size names to the
+ * PPD file...
  */
 
 static void
-ppd_defaults(ppd_file_t  *ppd,         /* I - PPD file */
-             ppd_group_t *g)           /* I - Group to default */
+ppd_mark_size(ppd_file_t *ppd,         /* I - PPD file */
+              const char *size)                /* I - Size name */
 {
-  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);
-}
+  int                  i;              /* Looping var */
+  _cups_pwg_media_t    *pwgmedia;      /* PWG media information */
+  ppd_size_t           *ppdsize;       /* Current PPD size */
+  double               dw, dl;         /* Difference in width and height */
+  double               width,          /* Width to find */
+                       length;         /* Length to find */
+  char                 width_str[256], /* Width in size name */
+                       length_str[256],/* Length in size name */
+                       units[256],     /* Units in size name */
+                       custom[256];    /* Custom size */
+  struct lconv         *loc;           /* Localization data */
 
 
-/*
- * 'ppd_mark_choices()' - Mark one or more option choices from a string.
- */
-
-static int                             /* O - 1 if there are conflicts, 0 otherwise */
-ppd_mark_choices(ppd_file_t *ppd,      /* I - PPD file */
-                 const char *options)  /* I - "*Option Choice ..." string */
-{
-  char option[PPD_MAX_NAME],           /* Current option */
-       choice[PPD_MAX_NAME],           /* Current choice */
-       *ptr;                           /* Pointer into option or choice */
-  int  conflict = 0;                   /* Do we have a conflict? */
-
-
-  if (!options)
-    return (0);
-
  /*
-  * Read all of the "*Option Choice" pairs from the string, marking PPD
-  * options as we go...
+  * See if this is a PPD size...
   */
 
-  while (*options)
+  if (!strncasecmp(size, "Custom.", 7) || ppdPageSize(ppd, size))
   {
-   /*
-    * Skip leading whitespace...
-    */
-
-    while (isspace(*options & 255))
-      options ++;
-
-    if (*options != '*')
-      break;
-
-   /*
-    * Get the option name...
-    */
-
-    options ++;
-    ptr = option;
-    while (*options && !isspace(*options & 255) &&
-              ptr < (option + sizeof(option) - 1))
-      *ptr++ = *options++;
+    ppd_mark_option(ppd, "PageSize", size);
+    return;
+  }
 
-    if (ptr == option)
-      break;
+ /*
+  * Nope, try looking up the PWG or legacy (IPP/ISO) size name...
+  */
 
-    *ptr = '\0';
+  if ((pwgmedia = _cupsPWGMediaByName(size)) == NULL)
+    pwgmedia = _cupsPWGMediaByLegacy(size);
 
+  if (pwgmedia)
+  {
+    width  = pwgmedia->width;
+    length = pwgmedia->length;
+  }
+  else if (sscanf(size, "%*[^_]_%*[^_]_%255[0-9.]x%255[0-9.]%s", width_str,
+                  length_str, units) == 3)
+  {
    /*
-    * Get the choice...
+    * Got a "self-describing" name that isn't in our table...
     */
 
-    while (isspace(*options & 255))
-      options ++;
-
-    if (!*options)
-      break;
+    loc    = localeconv();
+    width  = _cupsStrScand(width_str, NULL, loc);
+    length = _cupsStrScand(length_str, NULL, loc);
 
-    ptr = choice;
-    while (*options && !isspace(*options & 255) &&
-              ptr < (choice + sizeof(choice) - 1))
-      *ptr++ = *options++;
+    if (!strcmp(units, "in"))
+    {
+      width  *= 72.0;
+      length *= 72.0;
+    }
+    else if (!strcmp(units, "mm"))
+    {
+      width  *= 25.4 / 72.0;
+      length *= 25.4 / 72.0;
+    }
+    else
+      return;
+  }
+  else
+    return;
 
-    *ptr = '\0';
+ /*
+  * Search the PPD file for a matching size...
+  */
 
-   /*
-    * Mark the option...
-    */
+  for (i = ppd->num_sizes, ppdsize = ppd->sizes; i > 0; i --, ppdsize ++)
+  {
+    dw = ppdsize->width - width;
+    dl = ppdsize->length - length;
 
-    if (ppdMarkOption(ppd, option, choice))
-      conflict = 1;
+    if (dw > -5.0 && dw < 5.0 && dl > -5.0 && dl < 5.0)
+    {
+      ppd_mark_option(ppd, "PageSize", ppdsize->name);
+      return;
+    }
   }
 
  /*
-  * Return whether we had any conflicts...
+  * No match found; if custom sizes are supported, set a custom size...
   */
 
-  return (conflict);
+  if (ppd->variable_sizes)
+  {
+    snprintf(custom, sizeof(custom), "Custom.%dx%d", (int)width, (int)length);
+    ppd_mark_option(ppd, "PageSize", custom);
+  }
 }
 
 
 /*
- * End of "$Id: mark.c 7278 2008-01-31 01:23:09Z mike $".
+ * End of "$Id: mark.c 8210 2009-01-09 02:30:26Z mike $".
  */