]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/conflicts.c
Merge changes from CUPS 1.4svn-r8469.
[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_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@
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@
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 num_newopts; /* Number of new options */
185 cups_option_t *newopts; /* New options */
186 cups_array_t *active, /* Active constraints */
187 *pass, /* Resolvers for this pass */
188 *resolvers, /* Resolvers we have used */
189 *test; /* Test array for conflicts */
190 _ppd_cups_uiconsts_t *consts; /* Current constraints */
191 _ppd_cups_uiconst_t *constptr; /* Current constraint */
192 ppd_attr_t *resolver; /* Current resolver */
193 const char *resval; /* Pointer into resolver value */
194 char resoption[PPD_MAX_NAME],
195 /* Current resolver option */
196 reschoice[PPD_MAX_NAME],
197 /* Current resolver choice */
198 *resptr; /* Pointer into option/choice */
199 const char *value; /* Selected option value */
200 int changed; /* Did we change anything? */
201 ppd_choice_t *marked; /* Marked choice */
202
203
204 /*
205 * Range check input...
206 */
207
208 if (!ppd || !num_options || !options || (option == NULL) != (choice == NULL))
209 return (0);
210
211 /*
212 * Build a shadow option array...
213 */
214
215 num_newopts = 0;
216 newopts = NULL;
217
218 for (i = 0; i < *num_options; i ++)
219 num_newopts = cupsAddOption((*options)[i].name, (*options)[i].value,
220 num_newopts, &newopts);
221 if (option && strcasecmp(option, "Collate"))
222 num_newopts = cupsAddOption(option, choice, num_newopts, &newopts);
223
224 /*
225 * Loop until we have no conflicts...
226 */
227
228 cupsArraySave(ppd->sorted_attrs);
229
230 resolvers = NULL;
231 pass = cupsArrayNew((cups_array_func_t)strcasecmp, NULL);
232
233 while ((active = ppd_test_constraints(ppd, NULL, NULL, num_newopts, newopts,
234 _PPD_ALL_CONSTRAINTS)) != NULL)
235 {
236 if (!resolvers)
237 resolvers = cupsArrayNew((cups_array_func_t)strcasecmp, NULL);
238
239 for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active), changed = 0;
240 consts;
241 consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(active))
242 {
243 if (consts->resolver[0])
244 {
245 /*
246 * Look up the resolver...
247 */
248
249 if (cupsArrayFind(pass, consts->resolver))
250 continue; /* Already applied this resolver... */
251
252 if (cupsArrayFind(resolvers, consts->resolver))
253 {
254 /*
255 * Resolver loop!
256 */
257
258 DEBUG_printf(("ppdResolveConflicts: Resolver loop with %s!\n",
259 consts->resolver));
260 goto error;
261 }
262
263 if ((resolver = ppdFindAttr(ppd, "cupsUIResolver",
264 consts->resolver)) == NULL)
265 {
266 DEBUG_printf(("ppdResolveConflicts: Resolver %s not found!\n",
267 consts->resolver));
268 goto error;
269 }
270
271 if (!resolver->value)
272 {
273 DEBUG_printf(("ppdResolveConflicts: Resolver %s has no value!\n",
274 consts->resolver));
275 goto error;
276 }
277
278 /*
279 * Add the options from the resolver...
280 */
281
282 cupsArrayAdd(pass, consts->resolver);
283 cupsArrayAdd(resolvers, consts->resolver);
284
285 for (resval = resolver->value; *resval && !changed;)
286 {
287 while (isspace(*resval & 255))
288 resval ++;
289
290 if (*resval != '*')
291 break;
292
293 for (resval ++, resptr = resoption;
294 *resval && !isspace(*resval & 255);
295 resval ++)
296 if (resptr < (resoption + sizeof(resoption) - 1))
297 *resptr++ = *resval;
298
299 *resptr = '\0';
300
301 while (isspace(*resval & 255))
302 resval ++;
303
304 for (resptr = reschoice;
305 *resval && !isspace(*resval & 255);
306 resval ++)
307 if (resptr < (reschoice + sizeof(reschoice) - 1))
308 *resptr++ = *resval;
309
310 *resptr = '\0';
311
312 if (!resoption[0] || !reschoice[0])
313 break;
314
315 /*
316 * Is this the option we are changing?
317 */
318
319 if (option &&
320 (!strcasecmp(resoption, option) ||
321 (!strcasecmp(option, "PageSize") &&
322 !strcasecmp(resoption, "PageRegion")) ||
323 (!strcasecmp(option, "PageRegion") &&
324 !strcasecmp(resoption, "PageSize"))))
325 continue;
326
327 /*
328 * Try this choice...
329 */
330
331 if ((test = ppd_test_constraints(ppd, resoption, reschoice,
332 num_newopts, newopts,
333 _PPD_ALL_CONSTRAINTS)) == NULL)
334 {
335 /*
336 * That worked...
337 */
338
339 changed = 1;
340 }
341 else
342 cupsArrayDelete(test);
343
344 /*
345 * Add the option/choice from the resolver regardless of whether it
346 * worked; this makes sure that we can cascade several changes to
347 * make things resolve...
348 */
349
350 num_newopts = cupsAddOption(resoption, reschoice, num_newopts,
351 &newopts);
352 }
353 }
354 else
355 {
356 /*
357 * Try resolving by choosing the default values for non-installable
358 * options, then by iterating through the possible choices...
359 */
360
361 int j; /* Looping var */
362 ppd_choice_t *cptr; /* Current choice */
363 ppd_size_t *size; /* Current page size */
364
365
366 for (i = consts->num_constraints, constptr = consts->constraints;
367 i > 0 && !changed;
368 i --, constptr ++)
369 {
370 /*
371 * Can't resolve by changing an installable option...
372 */
373
374 if (constptr->installable)
375 continue;
376
377 /*
378 * Is this the option we are changing?
379 */
380
381 if (option &&
382 (!strcasecmp(constptr->option->keyword, option) ||
383 (!strcasecmp(option, "PageSize") &&
384 !strcasecmp(constptr->option->keyword, "PageRegion")) ||
385 (!strcasecmp(option, "PageRegion") &&
386 !strcasecmp(constptr->option->keyword, "PageSize"))))
387 continue;
388
389 /*
390 * Get the current option choice...
391 */
392
393 if ((value = cupsGetOption(constptr->option->keyword, num_newopts,
394 newopts)) == NULL)
395 {
396 if (!strcasecmp(constptr->option->keyword, "PageSize") ||
397 !strcasecmp(constptr->option->keyword, "PageRegion"))
398 {
399 if ((value = cupsGetOption("PageSize", num_newopts,
400 newopts)) == NULL)
401 value = cupsGetOption("PageRegion", num_newopts, newopts);
402
403 if (!value)
404 {
405 if ((size = ppdPageSize(ppd, NULL)) != NULL)
406 value = size->name;
407 else
408 value = "";
409 }
410 }
411 else
412 {
413 marked = ppdFindMarkedChoice(ppd, constptr->option->keyword);
414 value = marked ? marked->choice : "";
415 }
416 }
417
418 if (!strncasecmp(value, "Custom.", 7))
419 value = "Custom";
420
421 /*
422 * Try the default choice...
423 */
424
425 test = NULL;
426
427 if (strcasecmp(value, constptr->option->defchoice) &&
428 (test = ppd_test_constraints(ppd, constptr->option->keyword,
429 constptr->option->defchoice,
430 num_newopts, newopts,
431 _PPD_ALL_CONSTRAINTS)) == NULL)
432 {
433 /*
434 * That worked...
435 */
436
437 num_newopts = cupsAddOption(constptr->option->keyword,
438 constptr->option->defchoice,
439 num_newopts, &newopts);
440 changed = 1;
441 }
442 else
443 {
444 /*
445 * Try each choice instead...
446 */
447
448 for (j = constptr->option->num_choices,
449 cptr = constptr->option->choices;
450 j > 0;
451 j --, cptr ++)
452 {
453 cupsArrayDelete(test);
454 test = NULL;
455
456 if (strcasecmp(value, cptr->choice) &&
457 strcasecmp(constptr->option->defchoice, cptr->choice) &&
458 strcasecmp("Custom", cptr->choice) &&
459 (test = ppd_test_constraints(ppd, constptr->option->keyword,
460 cptr->choice, num_newopts,
461 newopts,
462 _PPD_ALL_CONSTRAINTS)) == NULL)
463 {
464 /*
465 * This choice works...
466 */
467
468 num_newopts = cupsAddOption(constptr->option->keyword,
469 cptr->choice, num_newopts,
470 &newopts);
471 changed = 1;
472 break;
473 }
474 }
475
476 cupsArrayDelete(test);
477 }
478 }
479 }
480
481 if (!changed)
482 {
483 DEBUG_puts("ppdResolveConflicts: Unable to automatically resolve "
484 "constraint!");
485 goto error;
486 }
487 }
488
489 cupsArrayClear(pass);
490 cupsArrayDelete(active);
491 }
492
493 /*
494 * Free the caller's option array...
495 */
496
497 cupsFreeOptions(*num_options, *options);
498
499 /*
500 * If Collate is the option we are testing, add it here. Otherwise, remove
501 * any Collate option from the resolve list since the filters automatically
502 * handle manual collation...
503 */
504
505 if (option && !strcasecmp(option, "Collate"))
506 num_newopts = cupsAddOption(option, choice, num_newopts, &newopts);
507 else
508 num_newopts = cupsRemoveOption("Collate", num_newopts, &newopts);
509
510 /*
511 * Return the new list of options to the caller...
512 */
513
514 *num_options = num_newopts;
515 *options = newopts;
516
517 cupsArrayDelete(pass);
518 cupsArrayDelete(resolvers);
519
520 cupsArrayRestore(ppd->sorted_attrs);
521
522 DEBUG_printf(("cupsResolveConflicts: Returning %d options:", num_newopts));
523 #ifdef DEBUG
524 for (i = 0; i < num_newopts; i ++)
525 DEBUG_printf(("cupsResolveConflicts: options[%d]: %s=%s", i,
526 newopts[i].name, newopts[i].value));
527 #endif /* DEBUG */
528
529 return (1);
530
531 /*
532 * If we get here, we failed to resolve...
533 */
534
535 error:
536
537 cupsFreeOptions(num_newopts, newopts);
538
539 cupsArrayDelete(active);
540 cupsArrayDelete(pass);
541 cupsArrayDelete(resolvers);
542
543 cupsArrayRestore(ppd->sorted_attrs);
544
545 DEBUG_puts("cupsResolveConflicts: Unable to resolve conflicts!");
546
547 return (0);
548 }
549
550
551 /*
552 * 'ppdConflicts()' - Check to see if there are any conflicts among the
553 * marked option choices.
554 *
555 * The returned value is the same as returned by @link ppdMarkOption@.
556 */
557
558 int /* O - Number of conflicts found */
559 ppdConflicts(ppd_file_t *ppd) /* I - PPD to check */
560 {
561 int i, /* Looping variable */
562 conflicts; /* Number of conflicts */
563 cups_array_t *active; /* Active conflicts */
564 _ppd_cups_uiconsts_t *c; /* Current constraints */
565 _ppd_cups_uiconst_t *cptr; /* Current constraint */
566 ppd_option_t *o; /* Current option */
567
568
569 if (!ppd)
570 return (0);
571
572 /*
573 * Clear all conflicts...
574 */
575
576 for (o = ppdFirstOption(ppd); o; o = ppdNextOption(ppd))
577 o->conflicted = 0;
578
579 /*
580 * Test for conflicts...
581 */
582
583 active = ppd_test_constraints(ppd, NULL, NULL, 0, NULL,
584 _PPD_ALL_CONSTRAINTS);
585 conflicts = cupsArrayCount(active);
586
587 /*
588 * Loop through all of the UI constraints and flag any options
589 * that conflict...
590 */
591
592 for (c = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active);
593 c;
594 c = (_ppd_cups_uiconsts_t *)cupsArrayNext(active))
595 {
596 for (i = c->num_constraints, cptr = c->constraints;
597 i > 0;
598 i --, cptr ++)
599 cptr->option->conflicted = 1;
600 }
601
602 cupsArrayDelete(active);
603
604 /*
605 * Return the number of conflicts found...
606 */
607
608 return (conflicts);
609 }
610
611
612 /*
613 * 'ppdInstallableConflict()' - Test whether an option choice conflicts with
614 * an installable option.
615 *
616 * This function tests whether a particular option choice is available based
617 * on constraints against options in the "InstallableOptions" group.
618 *
619 * @since CUPS 1.4@
620 */
621
622 int /* O - 1 if conflicting, 0 if not conflicting */
623 ppdInstallableConflict(
624 ppd_file_t *ppd, /* I - PPD file */
625 const char *option, /* I - Option */
626 const char *choice) /* I - Choice */
627 {
628 cups_array_t *active; /* Active conflicts */
629
630
631 /*
632 * Range check input...
633 */
634
635 if (!ppd || !option || !choice)
636 return (0);
637
638 /*
639 * Test constraints using the new option...
640 */
641
642 active = ppd_test_constraints(ppd, option, choice, 0, NULL,
643 _PPD_INSTALLABLE_CONSTRAINTS);
644
645 cupsArrayDelete(active);
646
647 return (active != NULL);
648 }
649
650
651 /*
652 * 'ppd_is_installable()' - Determine whether an option is in the
653 * InstallableOptions group.
654 */
655
656 static int /* O - 1 if installable, 0 if normal */
657 ppd_is_installable(
658 ppd_group_t *installable, /* I - InstallableOptions group */
659 const char *name) /* I - Option name */
660 {
661 if (installable)
662 {
663 int i; /* Looping var */
664 ppd_option_t *option; /* Current option */
665
666
667 for (i = installable->num_options, option = installable->options;
668 i > 0;
669 i --, option ++)
670 if (!strcasecmp(option->keyword, name))
671 return (1);
672 }
673
674 return (0);
675 }
676
677
678 /*
679 * 'ppd_load_constraints()' - Load constraints from a PPD file.
680 */
681
682 static void
683 ppd_load_constraints(ppd_file_t *ppd) /* I - PPD file */
684 {
685 int i; /* Looping var */
686 ppd_const_t *oldconst; /* Current UIConstraints data */
687 ppd_attr_t *constattr; /* Current cupsUIConstraints attribute */
688 _ppd_cups_uiconsts_t *consts; /* Current cupsUIConstraints data */
689 _ppd_cups_uiconst_t *constptr; /* Current constraint */
690 ppd_group_t *installable; /* Installable options group */
691 const char *vptr; /* Pointer into constraint value */
692 char option[PPD_MAX_NAME], /* Option name/MainKeyword */
693 choice[PPD_MAX_NAME], /* Choice/OptionKeyword */
694 *ptr; /* Pointer into option or choice */
695
696
697 /*
698 * Create an array to hold the constraint data...
699 */
700
701 ppd->cups_uiconstraints = cupsArrayNew(NULL, NULL);
702
703 /*
704 * Find the installable options group if it exists...
705 */
706
707 for (i = ppd->num_groups, installable = ppd->groups;
708 i > 0;
709 i --, installable ++)
710 if (!strcasecmp(installable->name, "InstallableOptions"))
711 break;
712
713 if (i <= 0)
714 installable = NULL;
715
716 /*
717 * Load old-style [Non]UIConstraints data...
718 */
719
720 for (i = ppd->num_consts, oldconst = ppd->consts; i > 0; i --, oldconst ++)
721 {
722 /*
723 * Weed out nearby duplicates, since the PPD spec requires that you
724 * define both "*Foo foo *Bar bar" and "*Bar bar *Foo foo"...
725 */
726
727 if (i > 1 &&
728 !strcasecmp(oldconst[0].option1, oldconst[1].option2) &&
729 !strcasecmp(oldconst[0].choice1, oldconst[1].choice2) &&
730 !strcasecmp(oldconst[0].option2, oldconst[1].option1) &&
731 !strcasecmp(oldconst[0].choice2, oldconst[1].choice1))
732 continue;
733
734 /*
735 * Allocate memory...
736 */
737
738 if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL)
739 {
740 DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
741 "UIConstraints!");
742 return;
743 }
744
745 if ((constptr = calloc(2, sizeof(_ppd_cups_uiconst_t))) == NULL)
746 {
747 free(consts);
748 DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
749 "UIConstraints!");
750 return;
751 }
752
753 /*
754 * Fill in the information...
755 */
756
757 consts->num_constraints = 2;
758 consts->constraints = constptr;
759
760 if (!strncasecmp(oldconst->option1, "Custom", 6) &&
761 !strcasecmp(oldconst->choice1, "True"))
762 {
763 constptr[0].option = ppdFindOption(ppd, oldconst->option1 + 6);
764 constptr[0].choice = ppdFindChoice(constptr[0].option, "Custom");
765 constptr[0].installable = 0;
766 }
767 else
768 {
769 constptr[0].option = ppdFindOption(ppd, oldconst->option1);
770 constptr[0].choice = ppdFindChoice(constptr[0].option,
771 oldconst->choice1);
772 constptr[0].installable = ppd_is_installable(installable,
773 oldconst->option1);
774 }
775
776 if (!constptr[0].option || (!constptr[0].choice && oldconst->choice1[0]))
777 {
778 DEBUG_printf(("ppd_load_constraints: Unknown option *%s %s!\n",
779 oldconst->option1, oldconst->choice1));
780 free(consts->constraints);
781 free(consts);
782 continue;
783 }
784
785 if (!strncasecmp(oldconst->option2, "Custom", 6) &&
786 !strcasecmp(oldconst->choice2, "True"))
787 {
788 constptr[1].option = ppdFindOption(ppd, oldconst->option2 + 6);
789 constptr[1].choice = ppdFindChoice(constptr[1].option, "Custom");
790 constptr[1].installable = 0;
791 }
792 else
793 {
794 constptr[1].option = ppdFindOption(ppd, oldconst->option2);
795 constptr[1].choice = ppdFindChoice(constptr[1].option,
796 oldconst->choice2);
797 constptr[1].installable = ppd_is_installable(installable,
798 oldconst->option2);
799 }
800
801 if (!constptr[1].option || (!constptr[1].choice && oldconst->choice2[0]))
802 {
803 DEBUG_printf(("ppd_load_constraints: Unknown option *%s %s!\n",
804 oldconst->option2, oldconst->choice2));
805 free(consts->constraints);
806 free(consts);
807 continue;
808 }
809
810 consts->installable = constptr[0].installable || constptr[1].installable;
811
812 /*
813 * Add it to the constraints array...
814 */
815
816 cupsArrayAdd(ppd->cups_uiconstraints, consts);
817 }
818
819 /*
820 * Then load new-style constraints...
821 */
822
823 for (constattr = ppdFindAttr(ppd, "cupsUIConstraints", NULL);
824 constattr;
825 constattr = ppdFindNextAttr(ppd, "cupsUIConstraints", NULL))
826 {
827 if (!constattr->value)
828 {
829 DEBUG_puts("ppd_load_constraints: Bad cupsUIConstraints value!");
830 continue;
831 }
832
833 for (i = 0, vptr = strchr(constattr->value, '*');
834 vptr;
835 i ++, vptr = strchr(vptr + 1, '*'));
836
837 if (i == 0)
838 {
839 DEBUG_puts("ppd_load_constraints: Bad cupsUIConstraints value!");
840 continue;
841 }
842
843 if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL)
844 {
845 DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
846 "cupsUIConstraints!");
847 return;
848 }
849
850 if ((constptr = calloc(i, sizeof(_ppd_cups_uiconst_t))) == NULL)
851 {
852 free(consts);
853 DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
854 "cupsUIConstraints!");
855 return;
856 }
857
858 consts->num_constraints = i;
859 consts->constraints = constptr;
860
861 strlcpy(consts->resolver, constattr->spec, sizeof(consts->resolver));
862
863 for (i = 0, vptr = strchr(constattr->value, '*');
864 vptr;
865 i ++, vptr = strchr(vptr, '*'), constptr ++)
866 {
867 /*
868 * Extract "*Option Choice" or just "*Option"...
869 */
870
871 for (vptr ++, ptr = option; *vptr && !isspace(*vptr & 255); vptr ++)
872 if (ptr < (option + sizeof(option) - 1))
873 *ptr++ = *vptr;
874
875 *ptr = '\0';
876
877 while (isspace(*vptr & 255))
878 vptr ++;
879
880 if (*vptr == '*')
881 choice[0] = '\0';
882 else
883 {
884 for (ptr = choice; *vptr && !isspace(*vptr & 255); vptr ++)
885 if (ptr < (choice + sizeof(choice) - 1))
886 *ptr++ = *vptr;
887
888 *ptr = '\0';
889 }
890
891 if (!strncasecmp(option, "Custom", 6) && !strcasecmp(choice, "True"))
892 {
893 _cups_strcpy(option, option + 6);
894 strcpy(choice, "Custom");
895 }
896
897 constptr->option = ppdFindOption(ppd, option);
898 constptr->choice = ppdFindChoice(constptr->option, choice);
899 constptr->installable = ppd_is_installable(installable, option);
900 consts->installable |= constptr->installable;
901
902 if (!constptr->option || (!constptr->choice && choice[0]))
903 {
904 DEBUG_printf(("ppd_load_constraints: Unknown option *%s %s!\n",
905 option, choice));
906 break;
907 }
908 }
909
910 if (!vptr)
911 cupsArrayAdd(ppd->cups_uiconstraints, consts);
912 else
913 {
914 free(consts->constraints);
915 free(consts);
916 }
917 }
918 }
919
920
921 /*
922 * 'ppd_test_constraints()' - See if any constraints are active.
923 */
924
925 static cups_array_t * /* O - Array of active constraints */
926 ppd_test_constraints(
927 ppd_file_t *ppd, /* I - PPD file */
928 const char *option, /* I - Current option */
929 const char *choice, /* I - Current choice */
930 int num_options, /* I - Number of additional options */
931 cups_option_t *options, /* I - Additional options */
932 int which) /* I - Which constraints to test */
933 {
934 int i; /* Looping var */
935 _ppd_cups_uiconsts_t *consts; /* Current constraints */
936 _ppd_cups_uiconst_t *constptr; /* Current constraint */
937 ppd_choice_t key, /* Search key */
938 *marked; /* Marked choice */
939 cups_array_t *active = NULL; /* Active constraints */
940 const char *value; /* Current value */
941
942
943 DEBUG_printf(("ppd_test_constraints(ppd=%p, num_options=%d, options=%p, "
944 "which=%d)\n", ppd, num_options, options, which));
945
946 if (!ppd->cups_uiconstraints)
947 ppd_load_constraints(ppd);
948
949 DEBUG_printf(("ppd_test_constraints: %d constraints!\n",
950 cupsArrayCount(ppd->cups_uiconstraints)));
951
952 cupsArraySave(ppd->marked);
953
954 for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(ppd->cups_uiconstraints);
955 consts;
956 consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(ppd->cups_uiconstraints))
957 {
958 DEBUG_printf(("ppd_test_constraints: installable=%d, resolver=\"%s\", "
959 "num_constraints=%d option1=\"%s\", choice1=\"%s\", "
960 "option2=\"%s\", choice2=\"%s\", ...\n",
961 consts->installable, consts->resolver, consts->num_constraints,
962 consts->constraints[0].option->keyword,
963 consts->constraints[0].choice ?
964 consts->constraints[0].choice->choice : "",
965 consts->constraints[1].option->keyword,
966 consts->constraints[1].choice ?
967 consts->constraints[1].choice->choice : ""));
968
969 if (which != _PPD_ALL_CONSTRAINTS && which != consts->installable)
970 continue;
971
972 DEBUG_puts("ppd_test_constraints: Testing...");
973
974 for (i = consts->num_constraints, constptr = consts->constraints;
975 i > 0;
976 i --, constptr ++)
977 {
978 DEBUG_printf(("ppd_test_constraints: %s=%s?\n", constptr->option->keyword,
979 constptr->choice ? constptr->choice->choice : ""));
980
981 if (constptr->choice &&
982 (!strcasecmp(constptr->option->keyword, "PageSize") ||
983 !strcasecmp(constptr->option->keyword, "PageRegion")))
984 {
985 /*
986 * PageSize and PageRegion are used depending on the selected input slot
987 * and manual feed mode. Validate against the selected page size instead
988 * of an individual option...
989 */
990
991 if (option && choice &&
992 (!strcasecmp(option, "PageSize") ||
993 !strcasecmp(option, "PageRegion")))
994 {
995 value = choice;
996 }
997 else if ((value = cupsGetOption("PageSize", num_options,
998 options)) == NULL)
999 if ((value = cupsGetOption("PageRegion", num_options,
1000 options)) == NULL)
1001 if ((value = cupsGetOption("media", num_options, options)) == NULL)
1002 {
1003 ppd_size_t *size = ppdPageSize(ppd, NULL);
1004
1005 if (size)
1006 value = size->name;
1007 }
1008
1009 if (value && !strncasecmp(value, "Custom.", 7))
1010 value = "Custom";
1011
1012 if (!value || strcasecmp(value, constptr->choice->choice))
1013 {
1014 DEBUG_puts("ppd_test_constraints: NO");
1015 break;
1016 }
1017 }
1018 else if (constptr->choice)
1019 {
1020 if (option && choice && !strcasecmp(option, constptr->option->keyword))
1021 {
1022 if (!strncasecmp(choice, "Custom.", 7))
1023 value = "Custom";
1024 else
1025 value = choice;
1026
1027 if (strcasecmp(value, constptr->choice->choice))
1028 {
1029 DEBUG_puts("ppd_test_constraints: NO");
1030 break;
1031 }
1032 }
1033 else if ((value = cupsGetOption(constptr->option->keyword, num_options,
1034 options)) != NULL)
1035 {
1036 if (!strncasecmp(value, "Custom.", 7))
1037 value = "Custom";
1038
1039 if (strcasecmp(value, constptr->choice->choice))
1040 {
1041 DEBUG_puts("ppd_test_constraints: NO");
1042 break;
1043 }
1044 }
1045 else if (!constptr->choice->marked)
1046 {
1047 DEBUG_puts("ppd_test_constraints: NO");
1048 break;
1049 }
1050 }
1051 else if (option && choice &&
1052 !strcasecmp(option, constptr->option->keyword))
1053 {
1054 if (!strcasecmp(choice, "None") || !strcasecmp(choice, "Off") ||
1055 !strcasecmp(choice, "False"))
1056 {
1057 DEBUG_puts("ppd_test_constraints: NO");
1058 break;
1059 }
1060 }
1061 else if ((value = cupsGetOption(constptr->option->keyword, num_options,
1062 options)) != NULL)
1063 {
1064 if (!strcasecmp(value, "None") || !strcasecmp(value, "Off") ||
1065 !strcasecmp(value, "False"))
1066 {
1067 DEBUG_puts("ppd_test_constraints: NO");
1068 break;
1069 }
1070 }
1071 else
1072 {
1073 key.option = constptr->option;
1074
1075 if ((marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key))
1076 == NULL ||
1077 (!strcasecmp(marked->choice, "None") ||
1078 !strcasecmp(marked->choice, "Off") ||
1079 !strcasecmp(marked->choice, "False")))
1080 {
1081 DEBUG_puts("ppd_test_constraints: NO");
1082 break;
1083 }
1084 }
1085 }
1086
1087 if (i <= 0)
1088 {
1089 if (!active)
1090 active = cupsArrayNew(NULL, NULL);
1091
1092 cupsArrayAdd(active, consts);
1093 DEBUG_puts("ppd_test_constraints: Added...");
1094 }
1095 }
1096
1097 cupsArrayRestore(ppd->marked);
1098
1099 DEBUG_printf(("ppd_test_constraints: Found %d active constraints!\n",
1100 cupsArrayCount(active)));
1101
1102 return (active);
1103 }
1104
1105
1106 /*
1107 * End of "$Id$".
1108 */