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