]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/conflicts.c
Merge changes from CUPS 1.5svn-r9491.
[thirdparty/cups.git] / cups / conflicts.c
1 /*
2 * "$Id$"
3 *
4 * Option marking routines for CUPS.
5 *
6 * Copyright 2007-2011 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * PostScript is a trademark of Adobe Systems, Inc.
16 *
17 * This file is subject to the Apple OS-Developed Software exception.
18 *
19 * Contents:
20 *
21 * cupsGetConflicts() - Get a list of conflicting options in a marked
22 * PPD.
23 * cupsResolveConflicts() - Resolve conflicts in a marked PPD.
24 * ppdConflicts() - Check to see if there are any conflicts among
25 * the marked option choices.
26 * ppdInstallableConflict() - Test whether an option choice conflicts with an
27 * installable option.
28 * ppd_is_installable() - Determine whether an option is in the
29 * InstallableOptions group.
30 * ppd_load_constraints() - Load constraints from a PPD file.
31 * ppd_test_constraints() - See if any constraints are active.
32 */
33
34 /*
35 * Include necessary headers...
36 */
37
38 #include "cups-private.h"
39 #include "ppd-private.h"
40
41
42 /*
43 * Local constants...
44 */
45
46 enum
47 {
48 _PPD_NORMAL_CONSTRAINTS,
49 _PPD_OPTION_CONSTRAINTS,
50 _PPD_INSTALLABLE_CONSTRAINTS,
51 _PPD_ALL_CONSTRAINTS
52 };
53
54
55 /*
56 * Local functions...
57 */
58
59 static int ppd_is_installable(ppd_group_t *installable,
60 const char *option);
61 static void ppd_load_constraints(ppd_file_t *ppd);
62 static cups_array_t *ppd_test_constraints(ppd_file_t *ppd,
63 const char *option,
64 const char *choice,
65 int num_options,
66 cups_option_t *options,
67 int which);
68
69
70 /*
71 * 'cupsGetConflicts()' - Get a list of conflicting options in a marked PPD.
72 *
73 * This function gets a list of options that would conflict if "option" and
74 * "choice" were marked in the PPD. You would typically call this function
75 * after marking the currently selected options in the PPD in order to
76 * determine whether a new option selection would cause a conflict.
77 *
78 * The number of conflicting options are returned with "options" pointing to
79 * the conflicting options. The returned option array must be freed using
80 * @link cupsFreeOptions@.
81 *
82 * @since CUPS 1.4/Mac OS X 10.6@
83 */
84
85 int /* O - Number of conflicting options */
86 cupsGetConflicts(
87 ppd_file_t *ppd, /* I - PPD file */
88 const char *option, /* I - Option to test */
89 const char *choice, /* I - Choice to test */
90 cups_option_t **options) /* O - Conflicting options */
91 {
92 int i, /* Looping var */
93 num_options; /* Number of conflicting options */
94 cups_array_t *active; /* Active conflicts */
95 _ppd_cups_uiconsts_t *c; /* Current constraints */
96 _ppd_cups_uiconst_t *cptr; /* Current constraint */
97
98
99 /*
100 * Range check input...
101 */
102
103 if (options)
104 *options = NULL;
105
106 if (!ppd || !option || !choice || !options)
107 return (0);
108
109 /*
110 * Test for conflicts...
111 */
112
113 active = ppd_test_constraints(ppd, option, choice, 0, NULL,
114 _PPD_ALL_CONSTRAINTS);
115
116 /*
117 * Loop through all of the UI constraints and add any options that conflict...
118 */
119
120 for (num_options = 0, c = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active);
121 c;
122 c = (_ppd_cups_uiconsts_t *)cupsArrayNext(active))
123 {
124 for (i = c->num_constraints, cptr = c->constraints;
125 i > 0;
126 i --, cptr ++)
127 if (strcasecmp(cptr->option->keyword, option))
128 num_options = cupsAddOption(cptr->option->keyword, cptr->choice->choice,
129 num_options, options);
130 }
131
132 cupsArrayDelete(active);
133
134 return (num_options);
135 }
136
137
138 /*
139 * 'cupsResolveConflicts()' - Resolve conflicts in a marked PPD.
140 *
141 * This function attempts to resolve any conflicts in a marked PPD, returning
142 * a list of option changes that are required to resolve them. On input,
143 * "num_options" and "options" contain any pending option changes that have
144 * not yet been marked, while "option" and "choice" contain the most recent
145 * selection which may or may not be in "num_options" or "options".
146 *
147 * On successful return, "num_options" and "options" are updated to contain
148 * "option" and "choice" along with any changes required to resolve conflicts
149 * specified in the PPD file and 1 is returned.
150 *
151 * If option conflicts cannot be resolved, "num_options" and "options" are not
152 * changed and 0 is returned.
153 *
154 * When resolving conflicts, @code cupsResolveConflicts@ does not consider
155 * changes to the current page size (@code media@, @code PageSize@, and
156 * @code PageRegion@) or to the most recent option specified in "option".
157 * Thus, if the only way to resolve a conflict is to change the page size
158 * or the option the user most recently changed, @code cupsResolveConflicts@
159 * will return 0 to indicate it was unable to resolve the conflicts.
160 *
161 * The @code cupsResolveConflicts@ function uses one of two sources of option
162 * constraint information. The preferred constraint information is defined by
163 * @code cupsUIConstraints@ and @code cupsUIResolver@ attributes - in this
164 * case, the PPD file provides constraint resolution actions.
165 *
166 * The backup constraint information is defined by the
167 * @code UIConstraints@ and @code NonUIConstraints@ attributes. These
168 * constraints are resolved algorithmically by first selecting the default
169 * choice for the conflicting option, then iterating over all possible choices
170 * until a non-conflicting option choice is found.
171 *
172 * @since CUPS 1.4/Mac OS X 10.6@
173 */
174
175 int /* O - 1 on success, 0 on failure */
176 cupsResolveConflicts(
177 ppd_file_t *ppd, /* I - PPD file */
178 const char *option, /* I - Newly selected option or @code NULL@ for none */
179 const char *choice, /* I - Newly selected choice or @code NULL@ for none */
180 int *num_options, /* IO - Number of additional selected options */
181 cups_option_t **options) /* IO - Additional selected options */
182 {
183 int i, /* Looping var */
184 tries, /* Number of tries */
185 num_newopts; /* Number of new options */
186 cups_option_t *newopts; /* New options */
187 cups_array_t *active, /* Active constraints */
188 *pass, /* Resolvers for this pass */
189 *resolvers, /* Resolvers we have used */
190 *test; /* Test array for conflicts */
191 _ppd_cups_uiconsts_t *consts; /* Current constraints */
192 _ppd_cups_uiconst_t *constptr; /* Current constraint */
193 ppd_attr_t *resolver; /* Current resolver */
194 const char *resval; /* Pointer into resolver value */
195 char resoption[PPD_MAX_NAME],
196 /* Current resolver option */
197 reschoice[PPD_MAX_NAME],
198 /* Current resolver choice */
199 *resptr; /* Pointer into option/choice */
200 const char *value; /* Selected option value */
201 int changed; /* Did we change anything? */
202 ppd_choice_t *marked; /* Marked choice */
203
204
205 /*
206 * Range check input...
207 */
208
209 if (!ppd || !num_options || !options || (option == NULL) != (choice == NULL))
210 return (0);
211
212 /*
213 * Build a shadow option array...
214 */
215
216 num_newopts = 0;
217 newopts = NULL;
218
219 for (i = 0; i < *num_options; i ++)
220 num_newopts = cupsAddOption((*options)[i].name, (*options)[i].value,
221 num_newopts, &newopts);
222 if (option && strcasecmp(option, "Collate"))
223 num_newopts = cupsAddOption(option, choice, num_newopts, &newopts);
224
225 /*
226 * Loop until we have no conflicts...
227 */
228
229 cupsArraySave(ppd->sorted_attrs);
230
231 resolvers = NULL;
232 pass = cupsArrayNew((cups_array_func_t)strcasecmp, NULL);
233 tries = 0;
234
235 while (tries < 100 &&
236 (active = ppd_test_constraints(ppd, NULL, NULL, num_newopts, newopts,
237 _PPD_ALL_CONSTRAINTS)) != NULL)
238 {
239 tries ++;
240
241 if (!resolvers)
242 resolvers = cupsArrayNew((cups_array_func_t)strcasecmp, NULL);
243
244 for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active), changed = 0;
245 consts;
246 consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(active))
247 {
248 if (consts->resolver[0])
249 {
250 /*
251 * Look up the resolver...
252 */
253
254 if (cupsArrayFind(pass, consts->resolver))
255 continue; /* Already applied this resolver... */
256
257 if (cupsArrayFind(resolvers, consts->resolver))
258 {
259 /*
260 * Resolver loop!
261 */
262
263 DEBUG_printf(("1ppdResolveConflicts: Resolver loop with %s!",
264 consts->resolver));
265 goto error;
266 }
267
268 if ((resolver = ppdFindAttr(ppd, "cupsUIResolver",
269 consts->resolver)) == NULL)
270 {
271 DEBUG_printf(("1ppdResolveConflicts: Resolver %s not found!",
272 consts->resolver));
273 goto error;
274 }
275
276 if (!resolver->value)
277 {
278 DEBUG_printf(("1ppdResolveConflicts: Resolver %s has no value!",
279 consts->resolver));
280 goto error;
281 }
282
283 /*
284 * Add the options from the resolver...
285 */
286
287 cupsArrayAdd(pass, consts->resolver);
288 cupsArrayAdd(resolvers, consts->resolver);
289
290 for (resval = resolver->value; *resval && !changed;)
291 {
292 while (_cups_isspace(*resval))
293 resval ++;
294
295 if (*resval != '*')
296 break;
297
298 for (resval ++, resptr = resoption;
299 *resval && !_cups_isspace(*resval);
300 resval ++)
301 if (resptr < (resoption + sizeof(resoption) - 1))
302 *resptr++ = *resval;
303
304 *resptr = '\0';
305
306 while (_cups_isspace(*resval))
307 resval ++;
308
309 for (resptr = reschoice;
310 *resval && !_cups_isspace(*resval);
311 resval ++)
312 if (resptr < (reschoice + sizeof(reschoice) - 1))
313 *resptr++ = *resval;
314
315 *resptr = '\0';
316
317 if (!resoption[0] || !reschoice[0])
318 break;
319
320 /*
321 * Is this the option we are changing?
322 */
323
324 if (option &&
325 (!strcasecmp(resoption, option) ||
326 (!strcasecmp(option, "PageSize") &&
327 !strcasecmp(resoption, "PageRegion")) ||
328 (!strcasecmp(option, "PageRegion") &&
329 !strcasecmp(resoption, "PageSize"))))
330 continue;
331
332 /*
333 * Try this choice...
334 */
335
336 if ((test = ppd_test_constraints(ppd, resoption, reschoice,
337 num_newopts, newopts,
338 _PPD_ALL_CONSTRAINTS)) == NULL)
339 {
340 /*
341 * That worked...
342 */
343
344 changed = 1;
345 }
346 else
347 cupsArrayDelete(test);
348
349 /*
350 * Add the option/choice from the resolver regardless of whether it
351 * worked; this makes sure that we can cascade several changes to
352 * make things resolve...
353 */
354
355 num_newopts = cupsAddOption(resoption, reschoice, num_newopts,
356 &newopts);
357 }
358 }
359 else
360 {
361 /*
362 * Try resolving by choosing the default values for non-installable
363 * options, then by iterating through the possible choices...
364 */
365
366 int j; /* Looping var */
367 ppd_choice_t *cptr; /* Current choice */
368 ppd_size_t *size; /* Current page size */
369
370
371 for (i = consts->num_constraints, constptr = consts->constraints;
372 i > 0 && !changed;
373 i --, constptr ++)
374 {
375 /*
376 * Can't resolve by changing an installable option...
377 */
378
379 if (constptr->installable)
380 continue;
381
382 /*
383 * Is this the option we are changing?
384 */
385
386 if (option &&
387 (!strcasecmp(constptr->option->keyword, option) ||
388 (!strcasecmp(option, "PageSize") &&
389 !strcasecmp(constptr->option->keyword, "PageRegion")) ||
390 (!strcasecmp(option, "PageRegion") &&
391 !strcasecmp(constptr->option->keyword, "PageSize"))))
392 continue;
393
394 /*
395 * Get the current option choice...
396 */
397
398 if ((value = cupsGetOption(constptr->option->keyword, num_newopts,
399 newopts)) == NULL)
400 {
401 if (!strcasecmp(constptr->option->keyword, "PageSize") ||
402 !strcasecmp(constptr->option->keyword, "PageRegion"))
403 {
404 if ((value = cupsGetOption("PageSize", num_newopts,
405 newopts)) == NULL)
406 value = cupsGetOption("PageRegion", num_newopts, newopts);
407
408 if (!value)
409 {
410 if ((size = ppdPageSize(ppd, NULL)) != NULL)
411 value = size->name;
412 else
413 value = "";
414 }
415 }
416 else
417 {
418 marked = ppdFindMarkedChoice(ppd, constptr->option->keyword);
419 value = marked ? marked->choice : "";
420 }
421 }
422
423 if (!strncasecmp(value, "Custom.", 7))
424 value = "Custom";
425
426 /*
427 * Try the default choice...
428 */
429
430 test = NULL;
431
432 if (strcasecmp(value, constptr->option->defchoice) &&
433 (test = ppd_test_constraints(ppd, constptr->option->keyword,
434 constptr->option->defchoice,
435 num_newopts, newopts,
436 _PPD_OPTION_CONSTRAINTS)) == NULL)
437 {
438 /*
439 * That worked...
440 */
441
442 num_newopts = cupsAddOption(constptr->option->keyword,
443 constptr->option->defchoice,
444 num_newopts, &newopts);
445 changed = 1;
446 }
447 else
448 {
449 /*
450 * Try each choice instead...
451 */
452
453 for (j = constptr->option->num_choices,
454 cptr = constptr->option->choices;
455 j > 0;
456 j --, cptr ++)
457 {
458 cupsArrayDelete(test);
459 test = NULL;
460
461 if (strcasecmp(value, cptr->choice) &&
462 strcasecmp(constptr->option->defchoice, cptr->choice) &&
463 strcasecmp("Custom", cptr->choice) &&
464 (test = ppd_test_constraints(ppd, constptr->option->keyword,
465 cptr->choice, num_newopts,
466 newopts,
467 _PPD_OPTION_CONSTRAINTS)) == NULL)
468 {
469 /*
470 * This choice works...
471 */
472
473 num_newopts = cupsAddOption(constptr->option->keyword,
474 cptr->choice, num_newopts,
475 &newopts);
476 changed = 1;
477 break;
478 }
479 }
480
481 cupsArrayDelete(test);
482 }
483 }
484 }
485 }
486
487 if (!changed)
488 {
489 DEBUG_puts("1ppdResolveConflicts: Unable to automatically resolve "
490 "constraint!");
491 goto error;
492 }
493
494 cupsArrayClear(pass);
495 cupsArrayDelete(active);
496 active = NULL;
497 }
498
499 if (tries >= 100)
500 goto error;
501
502 /*
503 * Free the caller's option array...
504 */
505
506 cupsFreeOptions(*num_options, *options);
507
508 /*
509 * If Collate is the option we are testing, add it here. Otherwise, remove
510 * any Collate option from the resolve list since the filters automatically
511 * handle manual collation...
512 */
513
514 if (option && !strcasecmp(option, "Collate"))
515 num_newopts = cupsAddOption(option, choice, num_newopts, &newopts);
516 else
517 num_newopts = cupsRemoveOption("Collate", num_newopts, &newopts);
518
519 /*
520 * Return the new list of options to the caller...
521 */
522
523 *num_options = num_newopts;
524 *options = newopts;
525
526 cupsArrayDelete(pass);
527 cupsArrayDelete(resolvers);
528
529 cupsArrayRestore(ppd->sorted_attrs);
530
531 DEBUG_printf(("1cupsResolveConflicts: Returning %d options:", num_newopts));
532 #ifdef DEBUG
533 for (i = 0; i < num_newopts; i ++)
534 DEBUG_printf(("1cupsResolveConflicts: options[%d]: %s=%s", i,
535 newopts[i].name, newopts[i].value));
536 #endif /* DEBUG */
537
538 return (1);
539
540 /*
541 * If we get here, we failed to resolve...
542 */
543
544 error:
545
546 cupsFreeOptions(num_newopts, newopts);
547
548 cupsArrayDelete(active);
549 cupsArrayDelete(pass);
550 cupsArrayDelete(resolvers);
551
552 cupsArrayRestore(ppd->sorted_attrs);
553
554 DEBUG_puts("1cupsResolveConflicts: Unable to resolve conflicts!");
555
556 return (0);
557 }
558
559
560 /*
561 * 'ppdConflicts()' - Check to see if there are any conflicts among the
562 * marked option choices.
563 *
564 * The returned value is the same as returned by @link ppdMarkOption@.
565 */
566
567 int /* O - Number of conflicts found */
568 ppdConflicts(ppd_file_t *ppd) /* I - PPD to check */
569 {
570 int i, /* Looping variable */
571 conflicts; /* Number of conflicts */
572 cups_array_t *active; /* Active conflicts */
573 _ppd_cups_uiconsts_t *c; /* Current constraints */
574 _ppd_cups_uiconst_t *cptr; /* Current constraint */
575 ppd_option_t *o; /* Current option */
576
577
578 if (!ppd)
579 return (0);
580
581 /*
582 * Clear all conflicts...
583 */
584
585 cupsArraySave(ppd->options);
586
587 for (o = ppdFirstOption(ppd); o; o = ppdNextOption(ppd))
588 o->conflicted = 0;
589
590 cupsArrayRestore(ppd->options);
591
592 /*
593 * Test for conflicts...
594 */
595
596 active = ppd_test_constraints(ppd, NULL, NULL, 0, NULL,
597 _PPD_ALL_CONSTRAINTS);
598 conflicts = cupsArrayCount(active);
599
600 /*
601 * Loop through all of the UI constraints and flag any options
602 * that conflict...
603 */
604
605 for (c = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active);
606 c;
607 c = (_ppd_cups_uiconsts_t *)cupsArrayNext(active))
608 {
609 for (i = c->num_constraints, cptr = c->constraints;
610 i > 0;
611 i --, cptr ++)
612 cptr->option->conflicted = 1;
613 }
614
615 cupsArrayDelete(active);
616
617 /*
618 * Return the number of conflicts found...
619 */
620
621 return (conflicts);
622 }
623
624
625 /*
626 * 'ppdInstallableConflict()' - Test whether an option choice conflicts with
627 * an installable option.
628 *
629 * This function tests whether a particular option choice is available based
630 * on constraints against options in the "InstallableOptions" group.
631 *
632 * @since CUPS 1.4/Mac OS X 10.6@
633 */
634
635 int /* O - 1 if conflicting, 0 if not conflicting */
636 ppdInstallableConflict(
637 ppd_file_t *ppd, /* I - PPD file */
638 const char *option, /* I - Option */
639 const char *choice) /* I - Choice */
640 {
641 cups_array_t *active; /* Active conflicts */
642
643
644 DEBUG_printf(("2ppdInstallableConflict(ppd=%p, option=\"%s\", choice=\"%s\")",
645 ppd, option, choice));
646
647 /*
648 * Range check input...
649 */
650
651 if (!ppd || !option || !choice)
652 return (0);
653
654 /*
655 * Test constraints using the new option...
656 */
657
658 active = ppd_test_constraints(ppd, option, choice, 0, NULL,
659 _PPD_INSTALLABLE_CONSTRAINTS);
660
661 cupsArrayDelete(active);
662
663 return (active != NULL);
664 }
665
666
667 /*
668 * 'ppd_is_installable()' - Determine whether an option is in the
669 * InstallableOptions group.
670 */
671
672 static int /* O - 1 if installable, 0 if normal */
673 ppd_is_installable(
674 ppd_group_t *installable, /* I - InstallableOptions group */
675 const char *name) /* I - Option name */
676 {
677 if (installable)
678 {
679 int i; /* Looping var */
680 ppd_option_t *option; /* Current option */
681
682
683 for (i = installable->num_options, option = installable->options;
684 i > 0;
685 i --, option ++)
686 if (!strcasecmp(option->keyword, name))
687 return (1);
688 }
689
690 return (0);
691 }
692
693
694 /*
695 * 'ppd_load_constraints()' - Load constraints from a PPD file.
696 */
697
698 static void
699 ppd_load_constraints(ppd_file_t *ppd) /* I - PPD file */
700 {
701 int i; /* Looping var */
702 ppd_const_t *oldconst; /* Current UIConstraints data */
703 ppd_attr_t *constattr; /* Current cupsUIConstraints attribute */
704 _ppd_cups_uiconsts_t *consts; /* Current cupsUIConstraints data */
705 _ppd_cups_uiconst_t *constptr; /* Current constraint */
706 ppd_group_t *installable; /* Installable options group */
707 const char *vptr; /* Pointer into constraint value */
708 char option[PPD_MAX_NAME], /* Option name/MainKeyword */
709 choice[PPD_MAX_NAME], /* Choice/OptionKeyword */
710 *ptr; /* Pointer into option or choice */
711
712
713 DEBUG_printf(("7ppd_load_constraints(ppd=%p)", ppd));
714
715 /*
716 * Create an array to hold the constraint data...
717 */
718
719 ppd->cups_uiconstraints = cupsArrayNew(NULL, NULL);
720
721 /*
722 * Find the installable options group if it exists...
723 */
724
725 for (i = ppd->num_groups, installable = ppd->groups;
726 i > 0;
727 i --, installable ++)
728 if (!strcasecmp(installable->name, "InstallableOptions"))
729 break;
730
731 if (i <= 0)
732 installable = NULL;
733
734 /*
735 * Load old-style [Non]UIConstraints data...
736 */
737
738 for (i = ppd->num_consts, oldconst = ppd->consts; i > 0; i --, oldconst ++)
739 {
740 /*
741 * Weed out nearby duplicates, since the PPD spec requires that you
742 * define both "*Foo foo *Bar bar" and "*Bar bar *Foo foo"...
743 */
744
745 if (i > 1 &&
746 !strcasecmp(oldconst[0].option1, oldconst[1].option2) &&
747 !strcasecmp(oldconst[0].choice1, oldconst[1].choice2) &&
748 !strcasecmp(oldconst[0].option2, oldconst[1].option1) &&
749 !strcasecmp(oldconst[0].choice2, oldconst[1].choice1))
750 continue;
751
752 /*
753 * Allocate memory...
754 */
755
756 if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL)
757 {
758 DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
759 "UIConstraints!");
760 return;
761 }
762
763 if ((constptr = calloc(2, sizeof(_ppd_cups_uiconst_t))) == NULL)
764 {
765 free(consts);
766 DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
767 "UIConstraints!");
768 return;
769 }
770
771 /*
772 * Fill in the information...
773 */
774
775 consts->num_constraints = 2;
776 consts->constraints = constptr;
777
778 if (!strncasecmp(oldconst->option1, "Custom", 6) &&
779 !strcasecmp(oldconst->choice1, "True"))
780 {
781 constptr[0].option = ppdFindOption(ppd, oldconst->option1 + 6);
782 constptr[0].choice = ppdFindChoice(constptr[0].option, "Custom");
783 constptr[0].installable = 0;
784 }
785 else
786 {
787 constptr[0].option = ppdFindOption(ppd, oldconst->option1);
788 constptr[0].choice = ppdFindChoice(constptr[0].option,
789 oldconst->choice1);
790 constptr[0].installable = ppd_is_installable(installable,
791 oldconst->option1);
792 }
793
794 if (!constptr[0].option || (!constptr[0].choice && oldconst->choice1[0]))
795 {
796 DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!",
797 oldconst->option1, oldconst->choice1));
798 free(consts->constraints);
799 free(consts);
800 continue;
801 }
802
803 if (!strncasecmp(oldconst->option2, "Custom", 6) &&
804 !strcasecmp(oldconst->choice2, "True"))
805 {
806 constptr[1].option = ppdFindOption(ppd, oldconst->option2 + 6);
807 constptr[1].choice = ppdFindChoice(constptr[1].option, "Custom");
808 constptr[1].installable = 0;
809 }
810 else
811 {
812 constptr[1].option = ppdFindOption(ppd, oldconst->option2);
813 constptr[1].choice = ppdFindChoice(constptr[1].option,
814 oldconst->choice2);
815 constptr[1].installable = ppd_is_installable(installable,
816 oldconst->option2);
817 }
818
819 if (!constptr[1].option || (!constptr[1].choice && oldconst->choice2[0]))
820 {
821 DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!",
822 oldconst->option2, oldconst->choice2));
823 free(consts->constraints);
824 free(consts);
825 continue;
826 }
827
828 consts->installable = constptr[0].installable || constptr[1].installable;
829
830 /*
831 * Add it to the constraints array...
832 */
833
834 cupsArrayAdd(ppd->cups_uiconstraints, consts);
835 }
836
837 /*
838 * Then load new-style constraints...
839 */
840
841 for (constattr = ppdFindAttr(ppd, "cupsUIConstraints", NULL);
842 constattr;
843 constattr = ppdFindNextAttr(ppd, "cupsUIConstraints", NULL))
844 {
845 if (!constattr->value)
846 {
847 DEBUG_puts("8ppd_load_constraints: Bad cupsUIConstraints value!");
848 continue;
849 }
850
851 for (i = 0, vptr = strchr(constattr->value, '*');
852 vptr;
853 i ++, vptr = strchr(vptr + 1, '*'));
854
855 if (i == 0)
856 {
857 DEBUG_puts("8ppd_load_constraints: Bad cupsUIConstraints value!");
858 continue;
859 }
860
861 if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL)
862 {
863 DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
864 "cupsUIConstraints!");
865 return;
866 }
867
868 if ((constptr = calloc(i, sizeof(_ppd_cups_uiconst_t))) == NULL)
869 {
870 free(consts);
871 DEBUG_puts("8ppd_load_constraints: Unable to allocate memory for "
872 "cupsUIConstraints!");
873 return;
874 }
875
876 consts->num_constraints = i;
877 consts->constraints = constptr;
878
879 strlcpy(consts->resolver, constattr->spec, sizeof(consts->resolver));
880
881 for (i = 0, vptr = strchr(constattr->value, '*');
882 vptr;
883 i ++, vptr = strchr(vptr, '*'), constptr ++)
884 {
885 /*
886 * Extract "*Option Choice" or just "*Option"...
887 */
888
889 for (vptr ++, ptr = option; *vptr && !_cups_isspace(*vptr); vptr ++)
890 if (ptr < (option + sizeof(option) - 1))
891 *ptr++ = *vptr;
892
893 *ptr = '\0';
894
895 while (_cups_isspace(*vptr))
896 vptr ++;
897
898 if (*vptr == '*')
899 choice[0] = '\0';
900 else
901 {
902 for (ptr = choice; *vptr && !_cups_isspace(*vptr); vptr ++)
903 if (ptr < (choice + sizeof(choice) - 1))
904 *ptr++ = *vptr;
905
906 *ptr = '\0';
907 }
908
909 if (!strncasecmp(option, "Custom", 6) && !strcasecmp(choice, "True"))
910 {
911 _cups_strcpy(option, option + 6);
912 strcpy(choice, "Custom");
913 }
914
915 constptr->option = ppdFindOption(ppd, option);
916 constptr->choice = ppdFindChoice(constptr->option, choice);
917 constptr->installable = ppd_is_installable(installable, option);
918 consts->installable |= constptr->installable;
919
920 if (!constptr->option || (!constptr->choice && choice[0]))
921 {
922 DEBUG_printf(("8ppd_load_constraints: Unknown option *%s %s!",
923 option, choice));
924 break;
925 }
926 }
927
928 if (!vptr)
929 cupsArrayAdd(ppd->cups_uiconstraints, consts);
930 else
931 {
932 free(consts->constraints);
933 free(consts);
934 }
935 }
936 }
937
938
939 /*
940 * 'ppd_test_constraints()' - See if any constraints are active.
941 */
942
943 static cups_array_t * /* O - Array of active constraints */
944 ppd_test_constraints(
945 ppd_file_t *ppd, /* I - PPD file */
946 const char *option, /* I - Current option */
947 const char *choice, /* I - Current choice */
948 int num_options, /* I - Number of additional options */
949 cups_option_t *options, /* I - Additional options */
950 int which) /* I - Which constraints to test */
951 {
952 int i; /* Looping var */
953 _ppd_cups_uiconsts_t *consts; /* Current constraints */
954 _ppd_cups_uiconst_t *constptr; /* Current constraint */
955 ppd_choice_t key, /* Search key */
956 *marked; /* Marked choice */
957 cups_array_t *active = NULL; /* Active constraints */
958 const char *value; /* Current value */
959
960
961 DEBUG_printf(("7ppd_test_constraints(ppd=%p, option=\"%s\", choice=\"%s\", "
962 "num_options=%d, options=%p, which=%d)", ppd, option, choice,
963 num_options, options, which));
964
965 if (!ppd->cups_uiconstraints)
966 ppd_load_constraints(ppd);
967
968 DEBUG_printf(("9ppd_test_constraints: %d constraints!",
969 cupsArrayCount(ppd->cups_uiconstraints)));
970
971 cupsArraySave(ppd->marked);
972
973 for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(ppd->cups_uiconstraints);
974 consts;
975 consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(ppd->cups_uiconstraints))
976 {
977 DEBUG_printf(("9ppd_test_constraints: installable=%d, resolver=\"%s\", "
978 "num_constraints=%d option1=\"%s\", choice1=\"%s\", "
979 "option2=\"%s\", choice2=\"%s\", ...",
980 consts->installable, consts->resolver, consts->num_constraints,
981 consts->constraints[0].option->keyword,
982 consts->constraints[0].choice ?
983 consts->constraints[0].choice->choice : "",
984 consts->constraints[1].option->keyword,
985 consts->constraints[1].choice ?
986 consts->constraints[1].choice->choice : ""));
987
988 if (consts->installable && which < _PPD_INSTALLABLE_CONSTRAINTS)
989 continue; /* Skip installable option constraint */
990
991 if (!consts->installable && which == _PPD_INSTALLABLE_CONSTRAINTS)
992 continue; /* Skip non-installable option constraint */
993
994 if (which == _PPD_OPTION_CONSTRAINTS && option)
995 {
996 /*
997 * Skip constraints that do not involve the current option...
998 */
999
1000 for (i = consts->num_constraints, constptr = consts->constraints;
1001 i > 0;
1002 i --, constptr ++)
1003 if (!strcasecmp(constptr->option->keyword, option))
1004 break;
1005
1006 if (!i)
1007 continue;
1008 }
1009
1010 DEBUG_puts("9ppd_test_constraints: Testing...");
1011
1012 for (i = consts->num_constraints, constptr = consts->constraints;
1013 i > 0;
1014 i --, constptr ++)
1015 {
1016 DEBUG_printf(("9ppd_test_constraints: %s=%s?", constptr->option->keyword,
1017 constptr->choice ? constptr->choice->choice : ""));
1018
1019 if (constptr->choice &&
1020 (!strcasecmp(constptr->option->keyword, "PageSize") ||
1021 !strcasecmp(constptr->option->keyword, "PageRegion")))
1022 {
1023 /*
1024 * PageSize and PageRegion are used depending on the selected input slot
1025 * and manual feed mode. Validate against the selected page size instead
1026 * of an individual option...
1027 */
1028
1029 if (option && choice &&
1030 (!strcasecmp(option, "PageSize") ||
1031 !strcasecmp(option, "PageRegion")))
1032 {
1033 value = choice;
1034 }
1035 else if ((value = cupsGetOption("PageSize", num_options,
1036 options)) == NULL)
1037 if ((value = cupsGetOption("PageRegion", num_options,
1038 options)) == NULL)
1039 if ((value = cupsGetOption("media", num_options, options)) == NULL)
1040 {
1041 ppd_size_t *size = ppdPageSize(ppd, NULL);
1042
1043 if (size)
1044 value = size->name;
1045 }
1046
1047 if (value && !strncasecmp(value, "Custom.", 7))
1048 value = "Custom";
1049
1050 if (!value || strcasecmp(value, constptr->choice->choice))
1051 {
1052 DEBUG_puts("9ppd_test_constraints: NO");
1053 break;
1054 }
1055 }
1056 else if (constptr->choice)
1057 {
1058 if (option && choice && !strcasecmp(option, constptr->option->keyword))
1059 {
1060 if (!strncasecmp(choice, "Custom.", 7))
1061 value = "Custom";
1062 else
1063 value = choice;
1064
1065 if (strcasecmp(value, constptr->choice->choice))
1066 {
1067 DEBUG_puts("9ppd_test_constraints: NO");
1068 break;
1069 }
1070 }
1071 else if ((value = cupsGetOption(constptr->option->keyword, num_options,
1072 options)) != NULL)
1073 {
1074 if (!strncasecmp(value, "Custom.", 7))
1075 value = "Custom";
1076
1077 if (strcasecmp(value, constptr->choice->choice))
1078 {
1079 DEBUG_puts("9ppd_test_constraints: NO");
1080 break;
1081 }
1082 }
1083 else if (!constptr->choice->marked)
1084 {
1085 DEBUG_puts("9ppd_test_constraints: NO");
1086 break;
1087 }
1088 }
1089 else if (option && choice &&
1090 !strcasecmp(option, constptr->option->keyword))
1091 {
1092 if (!strcasecmp(choice, "None") || !strcasecmp(choice, "Off") ||
1093 !strcasecmp(choice, "False"))
1094 {
1095 DEBUG_puts("9ppd_test_constraints: NO");
1096 break;
1097 }
1098 }
1099 else if ((value = cupsGetOption(constptr->option->keyword, num_options,
1100 options)) != NULL)
1101 {
1102 if (!strcasecmp(value, "None") || !strcasecmp(value, "Off") ||
1103 !strcasecmp(value, "False"))
1104 {
1105 DEBUG_puts("9ppd_test_constraints: NO");
1106 break;
1107 }
1108 }
1109 else
1110 {
1111 key.option = constptr->option;
1112
1113 if ((marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key))
1114 == NULL ||
1115 (!strcasecmp(marked->choice, "None") ||
1116 !strcasecmp(marked->choice, "Off") ||
1117 !strcasecmp(marked->choice, "False")))
1118 {
1119 DEBUG_puts("9ppd_test_constraints: NO");
1120 break;
1121 }
1122 }
1123 }
1124
1125 if (i <= 0)
1126 {
1127 if (!active)
1128 active = cupsArrayNew(NULL, NULL);
1129
1130 cupsArrayAdd(active, consts);
1131 DEBUG_puts("9ppd_test_constraints: Added...");
1132 }
1133 }
1134
1135 cupsArrayRestore(ppd->marked);
1136
1137 DEBUG_printf(("8ppd_test_constraints: Found %d active constraints!",
1138 cupsArrayCount(active)));
1139
1140 return (active);
1141 }
1142
1143
1144 /*
1145 * End of "$Id$".
1146 */