]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/conflicts.c
<rdar://problem/13655599> Seed: Print queue JOBS disappear after computer Wakes up...
[thirdparty/cups.git] / cups / conflicts.c
index 138a0c1199bb5d5708b27d953f766ec766725613..ae048d135e8ed44b296793eb7a2c7045cf0b9fdf 100644 (file)
@@ -1,9 +1,9 @@
 /*
  * "$Id$"
  *
- *   Option marking routines for the Common UNIX Printing System (CUPS).
+ *   Option marking routines for CUPS.
  *
- *   Copyright 2007-2009 by Apple Inc.
+ *   Copyright 2007-2012 by Apple Inc.
  *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  *   These coded instructions, statements, and computer programs are the
@@ -35,9 +35,8 @@
  * Include necessary headers...
  */
 
+#include "cups-private.h"
 #include "ppd-private.h"
-#include "string.h"
-#include "debug.h"
 
 
 /*
@@ -47,6 +46,7 @@
 enum
 {
   _PPD_NORMAL_CONSTRAINTS,
+  _PPD_OPTION_CONSTRAINTS,
   _PPD_INSTALLABLE_CONSTRAINTS,
   _PPD_ALL_CONSTRAINTS
 };
@@ -79,7 +79,7 @@ static cups_array_t   *ppd_test_constraints(ppd_file_t *ppd,
  * the conflicting options.  The returned option array must be freed using
  * @link cupsFreeOptions@.
  *
- * @since CUPS 1.4@
+ * @since CUPS 1.4/OS X 10.6@
  */
 
 int                                    /* O - Number of conflicting options */
@@ -94,6 +94,7 @@ cupsGetConflicts(
   cups_array_t         *active;        /* Active conflicts */
   _ppd_cups_uiconsts_t *c;             /* Current constraints */
   _ppd_cups_uiconst_t  *cptr;          /* Current constraint */
+  ppd_choice_t         *marked;        /* Marked choice */
 
 
  /*
@@ -124,9 +125,17 @@ cupsGetConflicts(
     for (i = c->num_constraints, cptr = c->constraints;
          i > 0;
         i --, cptr ++)
-      if (strcasecmp(cptr->option->keyword, option))
-        num_options = cupsAddOption(cptr->option->keyword, cptr->choice->choice,
-                                   num_options, options);
+      if (_cups_strcasecmp(cptr->option->keyword, option))
+      {
+        if (cptr->choice)
+         num_options = cupsAddOption(cptr->option->keyword,
+                                     cptr->choice->choice, num_options,
+                                     options);
+        else if ((marked = ppdFindMarkedChoice(ppd,
+                                              cptr->option->keyword)) != NULL)
+         num_options = cupsAddOption(cptr->option->keyword, marked->choice,
+                                     num_options, options);
+      }
   }
 
   cupsArrayDelete(active);
@@ -169,7 +178,7 @@ cupsGetConflicts(
  * choice for the conflicting option, then iterating over all possible choices
  * until a non-conflicting option choice is found.
  *
- * @since CUPS 1.4@
+ * @since CUPS 1.4/OS X 10.6@
  */
 
 int                                    /* O  - 1 on success, 0 on failure */
@@ -181,6 +190,7 @@ cupsResolveConflicts(
     cups_option_t **options)           /* IO - Additional selected options */
 {
   int                  i,              /* Looping var */
+                       tries,          /* Number of tries */
                        num_newopts;    /* Number of new options */
   cups_option_t                *newopts;       /* New options */
   cups_array_t         *active,        /* Active constraints */
@@ -195,7 +205,8 @@ cupsResolveConflicts(
                                        /* Current resolver option */
                        reschoice[PPD_MAX_NAME],
                                        /* Current resolver choice */
-                       *resptr;        /* Pointer into option/choice */
+                       *resptr,        /* Pointer into option/choice */
+                       firstpage[255]; /* AP_FIRSTPAGE_Keyword string */
   const char           *value;         /* Selected option value */
   int                  changed;        /* Did we change anything? */
   ppd_choice_t         *marked;        /* Marked choice */
@@ -218,7 +229,7 @@ cupsResolveConflicts(
   for (i = 0; i < *num_options; i ++)
     num_newopts = cupsAddOption((*options)[i].name, (*options)[i].value,
                                 num_newopts, &newopts);
-  if (option && strcasecmp(option, "Collate"))
+  if (option && _cups_strcasecmp(option, "Collate"))
     num_newopts = cupsAddOption(option, choice, num_newopts, &newopts);
 
  /*
@@ -228,13 +239,17 @@ cupsResolveConflicts(
   cupsArraySave(ppd->sorted_attrs);
 
   resolvers = NULL;
-  pass      = cupsArrayNew((cups_array_func_t)strcasecmp, NULL);
+  pass      = cupsArrayNew((cups_array_func_t)_cups_strcasecmp, NULL);
+  tries     = 0;
 
-  while ((active = ppd_test_constraints(ppd, NULL, NULL, num_newopts, newopts,
+  while (tries < 100 &&
+         (active = ppd_test_constraints(ppd, NULL, NULL, num_newopts, newopts,
                                         _PPD_ALL_CONSTRAINTS)) != NULL)
   {
+    tries ++;
+
     if (!resolvers)
-      resolvers = cupsArrayNew((cups_array_func_t)strcasecmp, NULL);
+      resolvers = cupsArrayNew((cups_array_func_t)_cups_strcasecmp, NULL);
 
     for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active), changed = 0;
          consts;
@@ -255,7 +270,7 @@ cupsResolveConflicts(
          * Resolver loop!
          */
 
-         DEBUG_printf(("ppdResolveConflicts: Resolver loop with %s!\n",
+         DEBUG_printf(("1cupsResolveConflicts: Resolver loop with %s!",
                        consts->resolver));
           goto error;
        }
@@ -263,14 +278,14 @@ cupsResolveConflicts(
         if ((resolver = ppdFindAttr(ppd, "cupsUIResolver",
                                    consts->resolver)) == NULL)
         {
-         DEBUG_printf(("ppdResolveConflicts: Resolver %s not found!\n",
+         DEBUG_printf(("1cupsResolveConflicts: Resolver %s not found!",
                        consts->resolver));
          goto error;
        }
 
         if (!resolver->value)
        {
-         DEBUG_printf(("ppdResolveConflicts: Resolver %s has no value!\n",
+         DEBUG_printf(("1cupsResolveConflicts: Resolver %s has no value!",
                        consts->resolver));
          goto error;
        }
@@ -284,25 +299,25 @@ cupsResolveConflicts(
 
         for (resval = resolver->value; *resval && !changed;)
        {
-         while (isspace(*resval & 255))
+         while (_cups_isspace(*resval))
            resval ++;
 
          if (*resval != '*')
            break;
 
          for (resval ++, resptr = resoption;
-              *resval && !isspace(*resval & 255);
+              *resval && !_cups_isspace(*resval);
               resval ++)
             if (resptr < (resoption + sizeof(resoption) - 1))
              *resptr++ = *resval;
 
           *resptr = '\0';
 
-         while (isspace(*resval & 255))
+         while (_cups_isspace(*resval))
            resval ++;
 
          for (resptr = reschoice;
-              *resval && !isspace(*resval & 255);
+              *resval && !_cups_isspace(*resval);
               resval ++)
             if (resptr < (reschoice + sizeof(reschoice) - 1))
              *resptr++ = *resval;
@@ -316,12 +331,23 @@ cupsResolveConflicts(
          * Is this the option we are changing?
          */
 
+          snprintf(firstpage, sizeof(firstpage), "AP_FIRSTPAGE_%s", resoption);
+
          if (option &&
-             (!strcasecmp(resoption, option) ||
-              (!strcasecmp(option, "PageSize") &&
-               !strcasecmp(resoption, "PageRegion")) ||
-              (!strcasecmp(option, "PageRegion") &&
-               !strcasecmp(resoption, "PageSize"))))
+             (!_cups_strcasecmp(resoption, option) ||
+              !_cups_strcasecmp(firstpage, option) ||
+              (!_cups_strcasecmp(option, "PageSize") &&
+               !_cups_strcasecmp(resoption, "PageRegion")) ||
+              (!_cups_strcasecmp(option, "AP_FIRSTPAGE_PageSize") &&
+               !_cups_strcasecmp(resoption, "PageSize")) ||
+              (!_cups_strcasecmp(option, "AP_FIRSTPAGE_PageSize") &&
+               !_cups_strcasecmp(resoption, "PageRegion")) ||
+              (!_cups_strcasecmp(option, "PageRegion") &&
+               !_cups_strcasecmp(resoption, "PageSize")) ||
+              (!_cups_strcasecmp(option, "AP_FIRSTPAGE_PageRegion") &&
+               !_cups_strcasecmp(resoption, "PageSize")) ||
+              (!_cups_strcasecmp(option, "AP_FIRSTPAGE_PageRegion") &&
+               !_cups_strcasecmp(resoption, "PageRegion"))))
            continue;
 
         /*
@@ -379,11 +405,11 @@ cupsResolveConflicts(
          */
 
          if (option &&
-             (!strcasecmp(constptr->option->keyword, option) ||
-              (!strcasecmp(option, "PageSize") &&
-               !strcasecmp(constptr->option->keyword, "PageRegion")) ||
-              (!strcasecmp(option, "PageRegion") &&
-               !strcasecmp(constptr->option->keyword, "PageSize"))))
+             (!_cups_strcasecmp(constptr->option->keyword, option) ||
+              (!_cups_strcasecmp(option, "PageSize") &&
+               !_cups_strcasecmp(constptr->option->keyword, "PageRegion")) ||
+              (!_cups_strcasecmp(option, "PageRegion") &&
+               !_cups_strcasecmp(constptr->option->keyword, "PageSize"))))
            continue;
 
          /*
@@ -393,8 +419,8 @@ cupsResolveConflicts(
           if ((value = cupsGetOption(constptr->option->keyword, num_newopts,
                                     newopts)) == NULL)
           {
-           if (!strcasecmp(constptr->option->keyword, "PageSize") ||
-               !strcasecmp(constptr->option->keyword, "PageRegion"))
+           if (!_cups_strcasecmp(constptr->option->keyword, "PageSize") ||
+               !_cups_strcasecmp(constptr->option->keyword, "PageRegion"))
            {
              if ((value = cupsGetOption("PageSize", num_newopts,
                                         newopts)) == NULL)
@@ -415,7 +441,7 @@ cupsResolveConflicts(
            }
          }
 
-         if (!strncasecmp(value, "Custom.", 7))
+         if (!_cups_strncasecmp(value, "Custom.", 7))
            value = "Custom";
 
          /*
@@ -424,11 +450,11 @@ cupsResolveConflicts(
 
           test = NULL;
 
-          if (strcasecmp(value, constptr->option->defchoice) &&
+          if (_cups_strcasecmp(value, constptr->option->defchoice) &&
              (test = ppd_test_constraints(ppd, constptr->option->keyword,
                                           constptr->option->defchoice,
                                           num_newopts, newopts,
-                                          _PPD_ALL_CONSTRAINTS)) == NULL)
+                                          _PPD_OPTION_CONSTRAINTS)) == NULL)
          {
           /*
            * That worked...
@@ -453,13 +479,13 @@ cupsResolveConflicts(
              cupsArrayDelete(test);
              test = NULL;
 
-             if (strcasecmp(value, cptr->choice) &&
-                 strcasecmp(constptr->option->defchoice, cptr->choice) &&
-                 strcasecmp("Custom", cptr->choice) &&
+             if (_cups_strcasecmp(value, cptr->choice) &&
+                 _cups_strcasecmp(constptr->option->defchoice, cptr->choice) &&
+                 _cups_strcasecmp("Custom", cptr->choice) &&
                  (test = ppd_test_constraints(ppd, constptr->option->keyword,
                                               cptr->choice, num_newopts,
                                               newopts,
-                                              _PPD_ALL_CONSTRAINTS)) == NULL)
+                                              _PPD_OPTION_CONSTRAINTS)) == NULL)
              {
               /*
                * This choice works...
@@ -477,19 +503,23 @@ cupsResolveConflicts(
           }
         }
       }
+    }
 
-      if (!changed)
-      {
-       DEBUG_puts("ppdResolveConflicts: Unable to automatically resolve "
-                  "constraint!");
-       goto error;
-      }
+    if (!changed)
+    {
+      DEBUG_puts("1cupsResolveConflicts: Unable to automatically resolve "
+                "constraint!");
+      goto error;
     }
 
     cupsArrayClear(pass);
     cupsArrayDelete(active);
+    active = NULL;
   }
 
+  if (tries >= 100)
+    goto error;
+
  /*
   * Free the caller's option array...
   */
@@ -502,7 +532,7 @@ cupsResolveConflicts(
   * handle manual collation...
   */
 
-  if (option && !strcasecmp(option, "Collate"))
+  if (option && !_cups_strcasecmp(option, "Collate"))
     num_newopts = cupsAddOption(option, choice, num_newopts, &newopts);
   else
     num_newopts = cupsRemoveOption("Collate", num_newopts, &newopts);
@@ -519,10 +549,10 @@ cupsResolveConflicts(
 
   cupsArrayRestore(ppd->sorted_attrs);
 
-  DEBUG_printf(("cupsResolveConflicts: Returning %d options:", num_newopts));
+  DEBUG_printf(("1cupsResolveConflicts: Returning %d options:", num_newopts));
 #ifdef DEBUG
   for (i = 0; i < num_newopts; i ++)
-    DEBUG_printf(("cupsResolveConflicts: options[%d]: %s=%s", i,
+    DEBUG_printf(("1cupsResolveConflicts: options[%d]: %s=%s", i,
                   newopts[i].name, newopts[i].value));
 #endif /* DEBUG */
 
@@ -536,12 +566,13 @@ cupsResolveConflicts(
 
   cupsFreeOptions(num_newopts, newopts);
 
+  cupsArrayDelete(active);
   cupsArrayDelete(pass);
   cupsArrayDelete(resolvers);
 
   cupsArrayRestore(ppd->sorted_attrs);
 
-  DEBUG_puts("cupsResolveConflicts: Unable to resolve conflicts!");
+  DEBUG_puts("1cupsResolveConflicts: Unable to resolve conflicts!");
 
   return (0);
 }
@@ -572,9 +603,13 @@ ppdConflicts(ppd_file_t *ppd)              /* I - PPD to check */
   * Clear all conflicts...
   */
 
+  cupsArraySave(ppd->options);
+
   for (o = ppdFirstOption(ppd); o; o = ppdNextOption(ppd))
     o->conflicted = 0;
 
+  cupsArrayRestore(ppd->options);
+
  /*
   * Test for conflicts...
   */
@@ -615,7 +650,7 @@ ppdConflicts(ppd_file_t *ppd)               /* I - PPD to check */
  * This function tests whether a particular option choice is available based
  * on constraints against options in the "InstallableOptions" group.
  *
- * @since CUPS 1.4@
+ * @since CUPS 1.4/OS X 10.6@
  */
 
 int                                    /* O - 1 if conflicting, 0 if not conflicting */
@@ -627,7 +662,10 @@ ppdInstallableConflict(
   cups_array_t *active;                /* Active conflicts */
 
 
- /* 
+  DEBUG_printf(("2ppdInstallableConflict(ppd=%p, option=\"%s\", choice=\"%s\")",
+                ppd, option, choice));
+
+ /*
   * Range check input...
   */
 
@@ -666,7 +704,7 @@ ppd_is_installable(
     for (i = installable->num_options, option = installable->options;
          i > 0;
         i --, option ++)
-      if (!strcasecmp(option->keyword, name))
+      if (!_cups_strcasecmp(option->keyword, name))
         return (1);
   }
 
@@ -693,6 +731,8 @@ ppd_load_constraints(ppd_file_t *ppd)       /* I - PPD file */
                *ptr;                   /* Pointer into option or choice */
 
 
+  DEBUG_printf(("7ppd_load_constraints(ppd=%p)", ppd));
+
  /*
   * Create an array to hold the constraint data...
   */
@@ -706,7 +746,7 @@ ppd_load_constraints(ppd_file_t *ppd)       /* I - PPD file */
   for (i = ppd->num_groups, installable = ppd->groups;
        i > 0;
        i --, installable ++)
-    if (!strcasecmp(installable->name, "InstallableOptions"))
+    if (!_cups_strcasecmp(installable->name, "InstallableOptions"))
       break;
 
   if (i <= 0)
@@ -724,10 +764,10 @@ ppd_load_constraints(ppd_file_t *ppd)     /* I - PPD file */
     */
 
     if (i > 1 &&
-       !strcasecmp(oldconst[0].option1, oldconst[1].option2) &&
-       !strcasecmp(oldconst[0].choice1, oldconst[1].choice2) &&
-       !strcasecmp(oldconst[0].option2, oldconst[1].option1) &&
-       !strcasecmp(oldconst[0].choice2, oldconst[1].choice1))
+       !_cups_strcasecmp(oldconst[0].option1, oldconst[1].option2) &&
+       !_cups_strcasecmp(oldconst[0].choice1, oldconst[1].choice2) &&
+       !_cups_strcasecmp(oldconst[0].option2, oldconst[1].option1) &&
+       !_cups_strcasecmp(oldconst[0].choice2, oldconst[1].choice1))
       continue;
 
    /*
@@ -736,7 +776,7 @@ ppd_load_constraints(ppd_file_t *ppd)       /* I - PPD file */
 
     if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL)
     {
-      DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
+      DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
                 "UIConstraints!");
       return;
     }
@@ -744,7 +784,7 @@ ppd_load_constraints(ppd_file_t *ppd)       /* I - PPD file */
     if ((constptr = calloc(2, sizeof(_ppd_cups_uiconst_t))) == NULL)
     {
       free(consts);
-      DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
+      DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
                 "UIConstraints!");
       return;
     }
@@ -756,8 +796,8 @@ ppd_load_constraints(ppd_file_t *ppd)       /* I - PPD file */
     consts->num_constraints = 2;
     consts->constraints     = constptr;
 
-    if (!strncasecmp(oldconst->option1, "Custom", 6) &&
-       !strcasecmp(oldconst->choice1, "True"))
+    if (!_cups_strncasecmp(oldconst->option1, "Custom", 6) &&
+       !_cups_strcasecmp(oldconst->choice1, "True"))
     {
       constptr[0].option      = ppdFindOption(ppd, oldconst->option1 + 6);
       constptr[0].choice      = ppdFindChoice(constptr[0].option, "Custom");
@@ -774,15 +814,15 @@ ppd_load_constraints(ppd_file_t *ppd)     /* I - PPD file */
 
     if (!constptr[0].option || (!constptr[0].choice && oldconst->choice1[0]))
     {
-      DEBUG_printf(("ppd_load_constraints: Unknown option *%s %s!\n",
+      DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!",
                    oldconst->option1, oldconst->choice1));
       free(consts->constraints);
       free(consts);
       continue;
     }
 
-    if (!strncasecmp(oldconst->option2, "Custom", 6) &&
-       !strcasecmp(oldconst->choice2, "True"))
+    if (!_cups_strncasecmp(oldconst->option2, "Custom", 6) &&
+       !_cups_strcasecmp(oldconst->choice2, "True"))
     {
       constptr[1].option      = ppdFindOption(ppd, oldconst->option2 + 6);
       constptr[1].choice      = ppdFindChoice(constptr[1].option, "Custom");
@@ -799,7 +839,7 @@ ppd_load_constraints(ppd_file_t *ppd)       /* I - PPD file */
 
     if (!constptr[1].option || (!constptr[1].choice && oldconst->choice2[0]))
     {
-      DEBUG_printf(("ppd_load_constraints: Unknown option *%s %s!\n",
+      DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!",
                    oldconst->option2, oldconst->choice2));
       free(consts->constraints);
       free(consts);
@@ -825,7 +865,7 @@ ppd_load_constraints(ppd_file_t *ppd)       /* I - PPD file */
   {
     if (!constattr->value)
     {
-      DEBUG_puts("ppd_load_constraints: Bad cupsUIConstraints value!");
+      DEBUG_puts("8ppd_load_constraints: Bad cupsUIConstraints value!");
       continue;
     }
 
@@ -835,13 +875,13 @@ ppd_load_constraints(ppd_file_t *ppd)     /* I - PPD file */
 
     if (i == 0)
     {
-      DEBUG_puts("ppd_load_constraints: Bad cupsUIConstraints value!");
+      DEBUG_puts("8ppd_load_constraints: Bad cupsUIConstraints value!");
       continue;
     }
 
     if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL)
     {
-      DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
+      DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
                 "cupsUIConstraints!");
       return;
     }
@@ -849,7 +889,7 @@ ppd_load_constraints(ppd_file_t *ppd)       /* I - PPD file */
     if ((constptr = calloc(i, sizeof(_ppd_cups_uiconst_t))) == NULL)
     {
       free(consts);
-      DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
+      DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
                 "cupsUIConstraints!");
       return;
     }
@@ -867,30 +907,30 @@ ppd_load_constraints(ppd_file_t *ppd)     /* I - PPD file */
       * Extract "*Option Choice" or just "*Option"...
       */
 
-      for (vptr ++, ptr = option; *vptr && !isspace(*vptr & 255); vptr ++)
+      for (vptr ++, ptr = option; *vptr && !_cups_isspace(*vptr); vptr ++)
        if (ptr < (option + sizeof(option) - 1))
          *ptr++ = *vptr;
 
       *ptr = '\0';
 
-      while (isspace(*vptr & 255))
+      while (_cups_isspace(*vptr))
        vptr ++;
 
       if (*vptr == '*')
        choice[0] = '\0';
       else
       {
-       for (ptr = choice; *vptr && !isspace(*vptr & 255); vptr ++)
+       for (ptr = choice; *vptr && !_cups_isspace(*vptr); vptr ++)
          if (ptr < (choice + sizeof(choice) - 1))
            *ptr++ = *vptr;
 
        *ptr = '\0';
       }
 
-      if (!strncasecmp(option, "Custom", 6) && !strcasecmp(choice, "True"))
+      if (!_cups_strncasecmp(option, "Custom", 6) && !_cups_strcasecmp(choice, "True"))
       {
        _cups_strcpy(option, option + 6);
-       strcpy(choice, "Custom");
+       strlcpy(choice, "Custom", sizeof(choice));
       }
 
       constptr->option      = ppdFindOption(ppd, option);
@@ -900,7 +940,7 @@ ppd_load_constraints(ppd_file_t *ppd)       /* I - PPD file */
 
       if (!constptr->option || (!constptr->choice && choice[0]))
       {
-       DEBUG_printf(("ppd_load_constraints: Unknown option *%s %s!\n",
+       DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!",
                      option, choice));
        break;
       }
@@ -936,16 +976,19 @@ ppd_test_constraints(
   ppd_choice_t         key,            /* Search key */
                        *marked;        /* Marked choice */
   cups_array_t         *active = NULL; /* Active constraints */
-  const char           *value;         /* Current value */
+  const char           *value,         /* Current value */
+                       *firstvalue;    /* AP_FIRSTPAGE_Keyword value */
+  char                 firstpage[255]; /* AP_FIRSTPAGE_Keyword string */
 
 
-  DEBUG_printf(("ppd_test_constraints(ppd=%p, num_options=%d, options=%p, "
-                "which=%d)\n", ppd, num_options, options, which));
+  DEBUG_printf(("7ppd_test_constraints(ppd=%p, option=\"%s\", choice=\"%s\", "
+                "num_options=%d, options=%p, which=%d)", ppd, option, choice,
+               num_options, options, which));
 
   if (!ppd->cups_uiconstraints)
     ppd_load_constraints(ppd);
 
-  DEBUG_printf(("ppd_test_constraints: %d constraints!\n",
+  DEBUG_printf(("9ppd_test_constraints: %d constraints!",
                cupsArrayCount(ppd->cups_uiconstraints)));
 
   cupsArraySave(ppd->marked);
@@ -954,9 +997,9 @@ ppd_test_constraints(
        consts;
        consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(ppd->cups_uiconstraints))
   {
-    DEBUG_printf(("ppd_test_constraints: installable=%d, resolver=\"%s\", "
+    DEBUG_printf(("9ppd_test_constraints: installable=%d, resolver=\"%s\", "
                   "num_constraints=%d option1=\"%s\", choice1=\"%s\", "
-                 "option2=\"%s\", choice2=\"%s\", ...\n",
+                 "option2=\"%s\", choice2=\"%s\", ...",
                  consts->installable, consts->resolver, consts->num_constraints,
                  consts->constraints[0].option->keyword,
                  consts->constraints[0].choice ?
@@ -965,21 +1008,46 @@ ppd_test_constraints(
                  consts->constraints[1].choice ?
                      consts->constraints[1].choice->choice : ""));
 
-    if (which != _PPD_ALL_CONSTRAINTS && which != consts->installable)
-      continue;
+    if (consts->installable && which < _PPD_INSTALLABLE_CONSTRAINTS)
+      continue;                                /* Skip installable option constraint */
+
+    if (!consts->installable && which == _PPD_INSTALLABLE_CONSTRAINTS)
+      continue;                                /* Skip non-installable option constraint */
+
+    if (which == _PPD_OPTION_CONSTRAINTS && option)
+    {
+     /*
+      * Skip constraints that do not involve the current option...
+      */
+
+      for (i = consts->num_constraints, constptr = consts->constraints;
+          i > 0;
+          i --, constptr ++)
+      {
+        if (!_cups_strcasecmp(constptr->option->keyword, option))
+         break;
+
+        if (!_cups_strncasecmp(option, "AP_FIRSTPAGE_", 13) &&
+           !_cups_strcasecmp(constptr->option->keyword, option + 13))
+         break;
+      }
 
-    DEBUG_puts("ppd_test_constraints: Testing...");
+      if (!i)
+        continue;
+    }
+
+    DEBUG_puts("9ppd_test_constraints: Testing...");
 
     for (i = consts->num_constraints, constptr = consts->constraints;
          i > 0;
         i --, constptr ++)
     {
-      DEBUG_printf(("ppd_test_constraints: %s=%s?\n", constptr->option->keyword,
+      DEBUG_printf(("9ppd_test_constraints: %s=%s?", constptr->option->keyword,
                    constptr->choice ? constptr->choice->choice : ""));
 
       if (constptr->choice &&
-          (!strcasecmp(constptr->option->keyword, "PageSize") ||
-           !strcasecmp(constptr->option->keyword, "PageRegion")))
+          (!_cups_strcasecmp(constptr->option->keyword, "PageSize") ||
+           !_cups_strcasecmp(constptr->option->keyword, "PageRegion")))
       {
        /*
         * PageSize and PageRegion are used depending on the selected input slot
@@ -988,8 +1056,8 @@ ppd_test_constraints(
        */
 
         if (option && choice &&
-           (!strcasecmp(option, "PageSize") ||
-            !strcasecmp(option, "PageRegion")))
+           (!_cups_strcasecmp(option, "PageSize") ||
+            !_cups_strcasecmp(option, "PageRegion")))
        {
          value = choice;
         }
@@ -1005,79 +1073,118 @@ ppd_test_constraints(
                value = size->name;
            }
 
-        if (value && !strncasecmp(value, "Custom.", 7))
+        if (value && !_cups_strncasecmp(value, "Custom.", 7))
          value = "Custom";
 
-        if (!value || strcasecmp(value, constptr->choice->choice))
+        if (option && choice &&
+           (!_cups_strcasecmp(option, "AP_FIRSTPAGE_PageSize") ||
+            !_cups_strcasecmp(option, "AP_FIRSTPAGE_PageRegion")))
+       {
+         firstvalue = choice;
+        }
+       else if ((firstvalue = cupsGetOption("AP_FIRSTPAGE_PageSize",
+                                            num_options, options)) == NULL)
+         firstvalue = cupsGetOption("AP_FIRSTPAGE_PageRegion", num_options,
+                                    options);
+
+        if (firstvalue && !_cups_strncasecmp(firstvalue, "Custom.", 7))
+         firstvalue = "Custom";
+
+        if ((!value || _cups_strcasecmp(value, constptr->choice->choice)) &&
+           (!firstvalue || _cups_strcasecmp(firstvalue, constptr->choice->choice)))
        {
-         DEBUG_puts("ppd_test_constraints: NO");
+         DEBUG_puts("9ppd_test_constraints: NO");
          break;
        }
       }
       else if (constptr->choice)
       {
-        if (option && choice && !strcasecmp(option, constptr->option->keyword))
+       /*
+        * Compare against the constrained choice...
+       */
+
+        if (option && choice && !_cups_strcasecmp(option, constptr->option->keyword))
        {
-         if (!strncasecmp(choice, "Custom.", 7))
+         if (!_cups_strncasecmp(choice, "Custom.", 7))
            value = "Custom";
          else
            value = choice;
-
-         if (strcasecmp(value, constptr->choice->choice))
-         {
-           DEBUG_puts("ppd_test_constraints: NO");
-           break;
-         }
        }
         else if ((value = cupsGetOption(constptr->option->keyword, num_options,
                                        options)) != NULL)
         {
-         if (!strncasecmp(value, "Custom.", 7))
+         if (!_cups_strncasecmp(value, "Custom.", 7))
            value = "Custom";
+       }
+        else if (constptr->choice->marked)
+         value = constptr->choice->choice;
+       else
+         value = NULL;
 
-         if (strcasecmp(value, constptr->choice->choice))
-         {
-           DEBUG_puts("ppd_test_constraints: NO");
-           break;
-         }
+       /*
+        * Now check AP_FIRSTPAGE_option...
+       */
+
+        snprintf(firstpage, sizeof(firstpage), "AP_FIRSTPAGE_%s",
+                constptr->option->keyword);
+
+        if (option && choice && !_cups_strcasecmp(option, firstpage))
+       {
+         if (!_cups_strncasecmp(choice, "Custom.", 7))
+           firstvalue = "Custom";
+         else
+           firstvalue = choice;
+       }
+        else if ((firstvalue = cupsGetOption(firstpage, num_options,
+                                            options)) != NULL)
+        {
+         if (!_cups_strncasecmp(firstvalue, "Custom.", 7))
+           firstvalue = "Custom";
        }
-        else if (!constptr->choice->marked)
+       else
+         firstvalue = NULL;
+
+        DEBUG_printf(("9ppd_test_constraints: value=%s, firstvalue=%s", value,
+                     firstvalue));
+
+        if ((!value || _cups_strcasecmp(value, constptr->choice->choice)) &&
+           (!firstvalue || _cups_strcasecmp(firstvalue, constptr->choice->choice)))
        {
-         DEBUG_puts("ppd_test_constraints: NO");
+         DEBUG_puts("9ppd_test_constraints: NO");
          break;
        }
       }
       else if (option && choice &&
-               !strcasecmp(option, constptr->option->keyword))
+               !_cups_strcasecmp(option, constptr->option->keyword))
       {
-       if (!strcasecmp(choice, "None") || !strcasecmp(choice, "Off") ||
-           !strcasecmp(choice, "False"))
+       if (!_cups_strcasecmp(choice, "None") || !_cups_strcasecmp(choice, "Off") ||
+           !_cups_strcasecmp(choice, "False"))
        {
-         DEBUG_puts("ppd_test_constraints: NO");
-          break;
+         DEBUG_puts("9ppd_test_constraints: NO");
+         break;
        }
       }
       else if ((value = cupsGetOption(constptr->option->keyword, num_options,
-                                     options)) != NULL)
+                                     options)) != NULL)
       {
-       if (!strcasecmp(value, "None") || !strcasecmp(value, "Off") ||
-           !strcasecmp(value, "False"))
+       if (!_cups_strcasecmp(value, "None") || !_cups_strcasecmp(value, "Off") ||
+           !_cups_strcasecmp(value, "False"))
        {
-         DEBUG_puts("ppd_test_constraints: NO");
+         DEBUG_puts("9ppd_test_constraints: NO");
          break;
        }
       }
       else
       {
-        key.option = constptr->option;
+       key.option = constptr->option;
 
        if ((marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key))
-               == NULL ||
-           (!strcasecmp(marked->choice, "None") ||
-            !strcasecmp(marked->choice, "Off") ||
-            !strcasecmp(marked->choice, "False")))
+               == NULL ||
+           (!_cups_strcasecmp(marked->choice, "None") ||
+            !_cups_strcasecmp(marked->choice, "Off") ||
+            !_cups_strcasecmp(marked->choice, "False")))
        {
-         DEBUG_puts("ppd_test_constraints: NO");
+         DEBUG_puts("9ppd_test_constraints: NO");
          break;
        }
       }
@@ -1089,13 +1196,13 @@ ppd_test_constraints(
         active = cupsArrayNew(NULL, NULL);
 
       cupsArrayAdd(active, consts);
-      DEBUG_puts("ppd_test_constraints: Added...");
+      DEBUG_puts("9ppd_test_constraints: Added...");
     }
   }
 
   cupsArrayRestore(ppd->marked);
 
-  DEBUG_printf(("ppd_test_constraints: Found %d active constraints!\n",
+  DEBUG_printf(("8ppd_test_constraints: Found %d active constraints!",
                 cupsArrayCount(active)));
 
   return (active);