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