]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/conflicts.c
Merge changes from CUPS 1.4svn-r7791.
[thirdparty/cups.git] / cups / conflicts.c
1 /*
2 * "$Id$"
3 *
4 * Option marking routines for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2007-2008 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * PostScript is a trademark of Adobe Systems, Inc.
16 *
17 * This file is subject to the Apple OS-Developed Software exception.
18 *
19 * Contents:
20 *
21 * ppdConflicts() - Check to see if there are any conflicts among
22 * the marked option choices.
23 * ppdInstallableConflict() - Test whether an option choice conflicts with an
24 * installable option.
25 * cupsResolveConflicts() - Resolve conflicts in a marked PPD.
26 * ppd_is_installable() - Determine whether an option is in the
27 * InstallableOptions group.
28 * ppd_load_constraints() - Load constraints from a PPD file.
29 * ppd_test_constraints() - See if any constraints are active.
30 */
31
32 /*
33 * Include necessary headers...
34 */
35
36 #include "ppd-private.h"
37 #include "string.h"
38 #include "debug.h"
39
40
41 /*
42 * Local constants...
43 */
44
45 enum
46 {
47 _PPD_NORMAL_CONSTRAINTS,
48 _PPD_INSTALLABLE_CONSTRAINTS,
49 _PPD_ALL_CONSTRAINTS
50 };
51
52
53 /*
54 * Local functions...
55 */
56
57 static int ppd_is_installable(ppd_group_t *installable,
58 const char *option);
59 static void ppd_load_constraints(ppd_file_t *ppd);
60 static cups_array_t *ppd_test_constraints(ppd_file_t *ppd, int num_options,
61 cups_option_t *options,
62 int which);
63
64
65 /*
66 * 'ppdConflicts()' - Check to see if there are any conflicts among the
67 * marked option choices.
68 *
69 * The returned value is the same as returned by @link ppdMarkOption@.
70 */
71
72 int /* O - Number of conflicts found */
73 ppdConflicts(ppd_file_t *ppd) /* I - PPD to check */
74 {
75 int i, /* Looping variable */
76 conflicts; /* Number of conflicts */
77 cups_array_t *active; /* Active conflicts */
78 _ppd_cups_uiconsts_t *c; /* Current constraints */
79 _ppd_cups_uiconst_t *cptr; /* Current constraint */
80 ppd_option_t *o; /* Current option */
81
82
83 if (!ppd)
84 return (0);
85
86 /*
87 * Clear all conflicts...
88 */
89
90 for (o = ppdFirstOption(ppd); o; o = ppdNextOption(ppd))
91 o->conflicted = 0;
92
93 /*
94 * Test for conflicts...
95 */
96
97 active = ppd_test_constraints(ppd, 0, NULL, _PPD_ALL_CONSTRAINTS);
98 conflicts = cupsArrayCount(active);
99
100 /*
101 * Loop through all of the UI constraints and flag any options
102 * that conflict...
103 */
104
105 for (c = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active);
106 c;
107 c = (_ppd_cups_uiconsts_t *)cupsArrayNext(active))
108 {
109 for (i = c->num_constraints, cptr = c->constraints;
110 i > 0;
111 i --, cptr ++)
112 cptr->option->conflicted = 1;
113 }
114
115 cupsArrayDelete(active);
116
117 /*
118 * Return the number of conflicts found...
119 */
120
121 return (conflicts);
122 }
123
124
125 /*
126 * 'ppdInstallableConflict()' - Test whether an option choice conflicts with
127 * an installable option.
128 *
129 * This function tests whether a particular option choice is available based
130 * on constraints against options in the "InstallableOptions" group.
131 *
132 * @since CUPS 1.4@
133 */
134
135 int /* O - 1 if conflicting, 0 if not conflicting */
136 ppdInstallableConflict(
137 ppd_file_t *ppd, /* I - PPD file */
138 const char *option, /* I - Option */
139 const char *choice) /* I - Choice */
140 {
141 cups_array_t *active; /* Active conflicts */
142 cups_option_t test; /* Test against this option */
143
144
145 /*
146 * Range check input...
147 */
148
149 if (!ppd || !option || !choice)
150 return (0);
151
152 /*
153 * Test constraints using the new option...
154 */
155
156 test.name = (char *)option;
157 test.value = (char *)choice;
158 active = ppd_test_constraints(ppd, 1, &test,
159 _PPD_INSTALLABLE_CONSTRAINTS);
160
161 cupsArrayDelete(active);
162
163 return (active != NULL);
164 }
165
166
167 /*
168 * 'cupsResolveConflicts()' - Resolve conflicts in a marked PPD.
169 *
170 * This function attempts to resolve any conflicts in a marked PPD, returning
171 * a list of option changes that are required to resolve any conflicts. On
172 * input, "num_options" and "options" contain any pending option changes that
173 * have not yet been marked, while "option" and "choice" contain the most recent
174 * selection which may or may not be in "num_options" or "options".
175 *
176 * On successful return, "num_options" and "options" are updated to contain
177 * "option" and "choice" along with any changes required to resolve conflicts
178 * specified in the PPD file. If option conflicts cannot be resolved,
179 * "num_options" and "options" are not changed.
180 *
181 * @code ppdResolveConflicts@ uses one of two sources of option constraint
182 * information. The preferred constraint information is defined by
183 * @code cupsUIConstraints@ and @code cupsUIResolver@ attributes - in this
184 * case, the PPD file provides constraint resolution actions. In this case,
185 * it should not be possible for @ppdResolveConflicts@ to fail, however it
186 * will do so if a resolver loop is detected.
187 *
188 * The backup constraint infomration is defined by the
189 * @code UIConstraints@ and @code NonUIConstraints@ attributes. These
190 * constraints are resolved algorithmically by selecting the default choice
191 * for the conflicting option. Unfortunately, this method is far more likely
192 * to fail.
193 *
194 * @since CUPS 1.4@
195 */
196
197 int /* O - 1 on success, 0 on failure */
198 cupsResolveConflicts(
199 ppd_file_t *ppd, /* I - PPD file */
200 const char *option, /* I - Newly selected option or @code NULL@ for none */
201 const char *choice, /* I - Newly selected choice or @code NULL@ for none */
202 int *num_options, /* IO - Number of additional selected options */
203 cups_option_t **options) /* IO - Additional selected options */
204 {
205 int i, /* Looping var */
206 num_newopts; /* Number of new options */
207 cups_option_t *newopts; /* New options */
208 cups_array_t *active, /* Active constraints */
209 *pass, /* Resolvers for this pass */
210 *resolvers; /* Resolvers we have used */
211 _ppd_cups_uiconsts_t *consts; /* Current constraints */
212 _ppd_cups_uiconst_t *constptr; /* Current constraint */
213 ppd_attr_t *resolver; /* Current resolver */
214 const char *value; /* Selected option value */
215 int changed; /* Did we change anything? */
216 ppd_choice_t *marked; /* Marked choice */
217 ppd_option_t *ignored; /* Ignored option */
218
219
220 /*
221 * Range check input...
222 */
223
224 if (!ppd || !num_options || !options || (option == NULL) != (choice == NULL))
225 return (0);
226
227 /*
228 * Build a shadow option array...
229 */
230
231 num_newopts = 0;
232 newopts = NULL;
233
234 for (i = 0; i < *num_options; i ++)
235 num_newopts = cupsAddOption((*options)[i].name, (*options)[i].value,
236 num_newopts, &newopts);
237 if (option && strcasecmp(option, "Collate"))
238 num_newopts = cupsAddOption(option, choice, num_newopts, &newopts);
239
240 /*
241 * Loop until we have no conflicts...
242 */
243
244 cupsArraySave(ppd->sorted_attrs);
245
246 resolvers = NULL;
247 pass = cupsArrayNew((cups_array_func_t)strcasecmp, NULL);
248
249 while ((active = ppd_test_constraints(ppd, num_newopts, newopts,
250 _PPD_ALL_CONSTRAINTS)) != NULL)
251 {
252 if (!resolvers)
253 resolvers = cupsArrayNew((cups_array_func_t)strcasecmp, NULL);
254
255 for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active), changed = 0;
256 consts;
257 consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(active))
258 {
259 if (consts->resolver[0])
260 {
261 /*
262 * Look up the resolver...
263 */
264
265 if (cupsArrayFind(pass, consts->resolver))
266 continue; /* Already applied this resolver... */
267
268 if (cupsArrayFind(resolvers, consts->resolver))
269 {
270 /*
271 * Resolver loop!
272 */
273
274 DEBUG_printf(("ppdResolveConflicts: Resolver loop with %s!\n",
275 consts->resolver));
276 goto error;
277 }
278
279 if ((resolver = ppdFindAttr(ppd, "cupsUIResolver",
280 consts->resolver)) == NULL)
281 {
282 DEBUG_printf(("ppdResolveConflicts: Resolver %s not found!\n",
283 consts->resolver));
284 goto error;
285 }
286
287 if (!resolver->value)
288 {
289 DEBUG_printf(("ppdResolveConflicts: Resolver %s has no value!\n",
290 consts->resolver));
291 goto error;
292 }
293
294 /*
295 * Add the options from the resolver...
296 */
297
298 cupsArrayAdd(pass, consts->resolver);
299 cupsArrayAdd(resolvers, consts->resolver);
300
301 num_newopts = _ppdParseOptions(resolver->value, num_newopts, &newopts);
302 changed = 1;
303 }
304 else
305 {
306 /*
307 * Try resolving by choosing the default values for non-installable
308 * options...
309 */
310
311 for (i = consts->num_constraints, constptr = consts->constraints,
312 ignored = NULL;
313 i > 0;
314 i --, constptr ++)
315 {
316 if (constptr->installable ||
317 !strcasecmp(constptr->option->keyword, "PageSize") ||
318 !strcasecmp(constptr->option->keyword, "PageRegion"))
319 continue;
320
321 if (option && !strcasecmp(constptr->option->keyword, option))
322 {
323 ignored = constptr->option;
324 continue;
325 }
326
327 if ((value = cupsGetOption(constptr->option->keyword, num_newopts,
328 newopts)) == NULL)
329 {
330 marked = ppdFindMarkedChoice(ppd, constptr->option->keyword);
331 value = marked ? marked->choice : "";
332 }
333
334 if (strcasecmp(value, constptr->option->defchoice))
335 {
336 num_newopts = cupsAddOption(constptr->option->keyword,
337 constptr->option->defchoice,
338 num_newopts, &newopts);
339 changed = 1;
340 }
341 }
342
343 if (ignored && !changed)
344 {
345 /*
346 * No choice, have to back out this selection...
347 */
348
349 if ((value = cupsGetOption(ignored->keyword, num_newopts,
350 newopts)) == NULL)
351 {
352 marked = ppdFindMarkedChoice(ppd, ignored->keyword);
353 value = marked ? marked->choice : "";
354 }
355
356 if (strcasecmp(value, ignored->defchoice))
357 {
358 num_newopts = cupsAddOption(ignored->keyword, ignored->defchoice,
359 num_newopts, &newopts);
360 changed = 1;
361 }
362 }
363 }
364
365 if (!changed)
366 {
367 DEBUG_puts("ppdResolveConflicts: Unable to automatically resolve "
368 "constraint!");
369 goto error;
370 }
371 }
372
373 cupsArrayClear(pass);
374 cupsArrayDelete(active);
375 }
376
377 /*
378 * Free the caller's option array...
379 */
380
381 cupsFreeOptions(*num_options, *options);
382
383 /*
384 * If Collate is the option we are testing, add it here. Otherwise, remove
385 * any Collate option from the resolve list since the filters automatically
386 * handle manual collation...
387 */
388
389 if (option && !strcasecmp(option, "Collate"))
390 num_newopts = cupsAddOption(option, choice, num_newopts, &newopts);
391 else
392 num_newopts = cupsRemoveOption("Collate", num_newopts, &newopts);
393
394 /*
395 * Return the new list of options to the caller...
396 */
397
398 *num_options = num_newopts;
399 *options = newopts;
400
401 cupsArrayDelete(pass);
402 cupsArrayDelete(resolvers);
403
404 cupsArrayRestore(ppd->sorted_attrs);
405
406 return (1);
407
408 /*
409 * If we get here, we failed to resolve...
410 */
411
412 error:
413
414 cupsFreeOptions(num_newopts, newopts);
415
416 cupsArrayDelete(pass);
417 cupsArrayDelete(resolvers);
418
419 cupsArrayRestore(ppd->sorted_attrs);
420
421 return (0);
422 }
423
424
425 /*
426 * 'ppd_is_installable()' - Determine whether an option is in the
427 * InstallableOptions group.
428 */
429
430 static int /* O - 1 if installable, 0 if normal */
431 ppd_is_installable(
432 ppd_group_t *installable, /* I - InstallableOptions group */
433 const char *name) /* I - Option name */
434 {
435 if (installable)
436 {
437 int i; /* Looping var */
438 ppd_option_t *option; /* Current option */
439
440
441 for (i = installable->num_options, option = installable->options;
442 i > 0;
443 i --, option ++)
444 if (!strcasecmp(option->keyword, name))
445 return (1);
446 }
447
448 return (0);
449 }
450
451
452 /*
453 * 'ppd_load_constraints()' - Load constraints from a PPD file.
454 */
455
456 static void
457 ppd_load_constraints(ppd_file_t *ppd) /* I - PPD file */
458 {
459 int i; /* Looping var */
460 ppd_const_t *oldconst; /* Current UIConstraints data */
461 ppd_attr_t *constattr; /* Current cupsUIConstraints attribute */
462 _ppd_cups_uiconsts_t *consts; /* Current cupsUIConstraints data */
463 _ppd_cups_uiconst_t *constptr; /* Current constraint */
464 ppd_group_t *installable; /* Installable options group */
465 const char *vptr; /* Pointer into constraint value */
466 char option[PPD_MAX_NAME], /* Option name/MainKeyword */
467 choice[PPD_MAX_NAME], /* Choice/OptionKeyword */
468 *ptr; /* Pointer into option or choice */
469
470
471 /*
472 * Create an array to hold the constraint data...
473 */
474
475 ppd->cups_uiconstraints = cupsArrayNew(NULL, NULL);
476
477 /*
478 * Find the installable options group if it exists...
479 */
480
481 for (i = ppd->num_groups, installable = ppd->groups;
482 i > 0;
483 i --, installable ++)
484 if (!strcasecmp(installable->name, "InstallableOptions"))
485 break;
486
487 if (i <= 0)
488 installable = NULL;
489
490 /*
491 * See what kind of constraint data we have in the PPD...
492 */
493
494 if ((constattr = ppdFindAttr(ppd, "cupsUIConstraints", NULL)) != NULL)
495 {
496 /*
497 * Load new-style cupsUIConstraints data...
498 */
499
500 for (; constattr;
501 constattr = ppdFindNextAttr(ppd, "cupsUIConstraints", NULL))
502 {
503 if (!constattr->value)
504 {
505 DEBUG_puts("ppd_load_constraints: Bad cupsUIConstraints value!");
506 continue;
507 }
508
509 for (i = 0, vptr = strchr(constattr->value, '*');
510 vptr;
511 i ++, vptr = strchr(vptr + 1, '*'));
512
513 if (i == 0)
514 {
515 DEBUG_puts("ppd_load_constraints: Bad cupsUIConstraints value!");
516 continue;
517 }
518
519 if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL)
520 {
521 DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
522 "cupsUIConstraints!");
523 return;
524 }
525
526 if ((constptr = calloc(i, sizeof(_ppd_cups_uiconst_t))) == NULL)
527 {
528 free(consts);
529 DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
530 "cupsUIConstraints!");
531 return;
532 }
533
534 consts->num_constraints = i;
535 consts->constraints = constptr;
536
537 strlcpy(consts->resolver, constattr->spec, sizeof(consts->resolver));
538
539 for (i = 0, vptr = strchr(constattr->value, '*');
540 vptr;
541 i ++, vptr = strchr(vptr, '*'), constptr ++)
542 {
543 /*
544 * Extract "*Option Choice" or just "*Option"...
545 */
546
547 for (vptr ++, ptr = option; *vptr && !isspace(*vptr & 255); vptr ++)
548 if (ptr < (option + sizeof(option) - 1))
549 *ptr++ = *vptr;
550
551 *ptr = '\0';
552
553 while (isspace(*vptr & 255))
554 vptr ++;
555
556 if (*vptr == '*')
557 choice[0] = '\0';
558 else
559 {
560 for (ptr = choice; *vptr && !isspace(*vptr & 255); vptr ++)
561 if (ptr < (choice + sizeof(choice) - 1))
562 *ptr++ = *vptr;
563
564 *ptr = '\0';
565 }
566
567 if (!strncasecmp(option, "Custom", 6) && !strcasecmp(choice, "True"))
568 {
569 _cups_strcpy(option, option + 6);
570 strcpy(choice, "Custom");
571 }
572
573 constptr->option = ppdFindOption(ppd, option);
574 constptr->choice = ppdFindChoice(constptr->option, choice);
575 constptr->installable = ppd_is_installable(installable, option);
576 consts->installable |= constptr->installable;
577
578 if (!constptr->option || (!constptr->choice && choice[0]))
579 {
580 DEBUG_printf(("ppd_load_constraints: Unknown option *%s %s!\n",
581 option, choice));
582 break;
583 }
584 }
585
586 if (!vptr)
587 cupsArrayAdd(ppd->cups_uiconstraints, consts);
588 else
589 {
590 free(consts->constraints);
591 free(consts);
592 }
593 }
594 }
595 else
596 {
597 /*
598 * Load old-style [Non]UIConstraints data...
599 */
600
601 for (i = ppd->num_consts, oldconst = ppd->consts; i > 0; i --, oldconst ++)
602 {
603 /*
604 * Weed out nearby duplicates, since the PPD spec requires that you
605 * define both "*Foo foo *Bar bar" and "*Bar bar *Foo foo"...
606 */
607
608 if (i > 1 &&
609 !strcasecmp(oldconst[0].option1, oldconst[1].option2) &&
610 !strcasecmp(oldconst[0].choice1, oldconst[1].choice2) &&
611 !strcasecmp(oldconst[0].option2, oldconst[1].option1) &&
612 !strcasecmp(oldconst[0].choice2, oldconst[1].choice1))
613 continue;
614
615 /*
616 * Allocate memory...
617 */
618
619 if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL)
620 {
621 DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
622 "UIConstraints!");
623 return;
624 }
625
626 if ((constptr = calloc(2, sizeof(_ppd_cups_uiconst_t))) == NULL)
627 {
628 free(consts);
629 DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
630 "UIConstraints!");
631 return;
632 }
633
634 /*
635 * Fill in the information...
636 */
637
638 consts->num_constraints = 2;
639 consts->constraints = constptr;
640
641 if (!strncasecmp(oldconst->option1, "Custom", 6) &&
642 !strcasecmp(oldconst->choice1, "True"))
643 {
644 constptr[0].option = ppdFindOption(ppd, oldconst->option1 + 6);
645 constptr[0].choice = ppdFindChoice(constptr[0].option, "Custom");
646 constptr[0].installable = 0;
647 }
648 else
649 {
650 constptr[0].option = ppdFindOption(ppd, oldconst->option1);
651 constptr[0].choice = ppdFindChoice(constptr[0].option,
652 oldconst->choice1);
653 constptr[0].installable = ppd_is_installable(installable,
654 oldconst->option1);
655 }
656
657 if (!constptr[0].option || (!constptr[0].choice && oldconst->choice1[0]))
658 {
659 DEBUG_printf(("ppd_load_constraints: Unknown option *%s %s!\n",
660 oldconst->option1, oldconst->choice1));
661 free(consts->constraints);
662 free(consts);
663 continue;
664 }
665
666 if (!strncasecmp(oldconst->option2, "Custom", 6) &&
667 !strcasecmp(oldconst->choice2, "True"))
668 {
669 constptr[1].option = ppdFindOption(ppd, oldconst->option2 + 6);
670 constptr[1].choice = ppdFindChoice(constptr[1].option, "Custom");
671 constptr[1].installable = 0;
672 }
673 else
674 {
675 constptr[1].option = ppdFindOption(ppd, oldconst->option2);
676 constptr[1].choice = ppdFindChoice(constptr[1].option,
677 oldconst->choice2);
678 constptr[1].installable = ppd_is_installable(installable,
679 oldconst->option2);
680 }
681
682 if (!constptr[1].option || (!constptr[1].choice && oldconst->choice2[0]))
683 {
684 DEBUG_printf(("ppd_load_constraints: Unknown option *%s %s!\n",
685 oldconst->option2, oldconst->choice2));
686 free(consts->constraints);
687 free(consts);
688 continue;
689 }
690
691 consts->installable = constptr[0].installable || constptr[1].installable;
692
693 /*
694 * Add it to the constraints array...
695 */
696
697 cupsArrayAdd(ppd->cups_uiconstraints, consts);
698 }
699 }
700 }
701
702
703
704 /*
705 * 'ppd_test_constraints()' - See if any constraints are active.
706 */
707
708 static cups_array_t * /* O - Array of active constraints */
709 ppd_test_constraints(
710 ppd_file_t *ppd, /* I - PPD file */
711 int num_options, /* I - Number of additional options */
712 cups_option_t *options, /* I - Additional options */
713 int which) /* I - Which constraints to test */
714 {
715 int i; /* Looping var */
716 _ppd_cups_uiconsts_t *consts; /* Current constraints */
717 _ppd_cups_uiconst_t *constptr; /* Current constraint */
718 ppd_choice_t key, /* Search key */
719 *marked; /* Marked choice */
720 cups_array_t *active = NULL; /* Active constraints */
721 const char *value; /* Current value */
722
723
724 if (!ppd->cups_uiconstraints)
725 ppd_load_constraints(ppd);
726
727 cupsArraySave(ppd->marked);
728
729 for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(ppd->cups_uiconstraints);
730 consts;
731 consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(ppd->cups_uiconstraints))
732 {
733 if (which != _PPD_ALL_CONSTRAINTS && which != consts->installable)
734 continue;
735
736 for (i = consts->num_constraints, constptr = consts->constraints;
737 i > 0;
738 i --, constptr ++)
739 {
740 if (constptr->choice &&
741 (!strcasecmp(constptr->option->keyword, "PageSize") ||
742 !strcasecmp(constptr->option->keyword, "PageRegion")))
743 {
744 /*
745 * PageSize and PageRegion are used depending on the selected input slot
746 * and manual feed mode. Validate against the selected page size instead
747 * of an individual option...
748 */
749
750 if ((value = cupsGetOption("PageSize", num_options, options)) == NULL)
751 if ((value = cupsGetOption("PageRegion", num_options,
752 options)) == NULL)
753 if ((value = cupsGetOption("media", num_options, options)) == NULL)
754 {
755 ppd_size_t *size = ppdPageSize(ppd, NULL);
756
757 if (size)
758 value = size->name;
759 }
760
761 if (!value || strcasecmp(value, constptr->choice->choice))
762 break;
763 }
764 else if (constptr->choice)
765 {
766 if ((value = cupsGetOption(constptr->option->keyword, num_options,
767 options)) != NULL)
768 {
769 if (strcasecmp(value, constptr->choice->choice))
770 break;
771 }
772 else if (!constptr->choice->marked)
773 break;
774 }
775 else if ((value = cupsGetOption(constptr->option->keyword, num_options,
776 options)) != NULL)
777 {
778 if (!strcasecmp(value, "None") || !strcasecmp(value, "Off") ||
779 !strcasecmp(value, "False"))
780 break;
781 }
782 else
783 {
784 key.option = constptr->option;
785
786 if ((marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key))
787 != NULL &&
788 (!strcasecmp(marked->choice, "None") ||
789 !strcasecmp(marked->choice, "Off") ||
790 !strcasecmp(marked->choice, "False")))
791 break;
792 }
793 }
794
795 if (i <= 0)
796 {
797 if (!active)
798 active = cupsArrayNew(NULL, NULL);
799
800 cupsArrayAdd(active, consts);
801 }
802 }
803
804 cupsArrayRestore(ppd->marked);
805
806 return (active);
807 }
808
809
810 /*
811 * End of "$Id$".
812 */