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