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