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