]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/conflicts.c
Merge changes from CUPS 1.4svn-r7770.
[thirdparty/cups.git] / cups / conflicts.c
CommitLineData
e78998df
MS
1/*
2 * "$Id$"
3 *
4 * Option marking routines for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2007-2008 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 * ppdConflicts() - Check to see if there are any conflicts among
22 * the marked option choices.
23 * ppdInstallableConflict() - Test whether an option choice conflicts with an
24 * installable option.
25 * cupsResolveConflicts() - Resolve conflicts in a marked PPD.
26 * ppd_is_installable() - Determine whether an option is in the
27 * InstallableOptions group.
28 * ppd_load_constraints() - Load constraints from a PPD file.
29 * ppd_test_constraints() - See if any constraints are active.
30 */
31
32/*
33 * Include necessary headers...
34 */
35
36#include "ppd-private.h"
37#include "string.h"
38#include "debug.h"
39
40
41/*
42 * Local constants...
43 */
44
45enum
46{
47 _PPD_NORMAL_CONSTRAINTS,
48 _PPD_INSTALLABLE_CONSTRAINTS,
49 _PPD_ALL_CONSTRAINTS
50};
51
52
53/*
54 * Local functions...
55 */
56
57static int ppd_is_installable(ppd_group_t *installable,
58 const char *option);
59static void ppd_load_constraints(ppd_file_t *ppd);
60static cups_array_t *ppd_test_constraints(ppd_file_t *ppd, int num_options,
61 cups_option_t *options,
62 int which);
63
64
65/*
66 * 'ppdConflicts()' - Check to see if there are any conflicts among the
67 * marked option choices.
68 *
69 * The returned value is the same as returned by @link ppdMarkOption@.
70 */
71
72int /* O - Number of conflicts found */
73ppdConflicts(ppd_file_t *ppd) /* I - PPD to check */
74{
75 int i, /* Looping variable */
76 conflicts; /* Number of conflicts */
77 cups_array_t *active; /* Active conflicts */
78 _ppd_cups_uiconsts_t *c; /* Current constraints */
79 _ppd_cups_uiconst_t *cptr; /* Current constraint */
80 ppd_option_t *o; /* Current option */
81
82
83 if (!ppd)
84 return (0);
85
86 /*
87 * Clear all conflicts...
88 */
89
e78998df
MS
90 for (o = ppdFirstOption(ppd); o; o = ppdNextOption(ppd))
91 o->conflicted = 0;
92
93 /*
94 * Test for conflicts...
95 */
96
97 active = ppd_test_constraints(ppd, 0, NULL, _PPD_ALL_CONSTRAINTS);
98 conflicts = cupsArrayCount(active);
99
100 /*
101 * Loop through all of the UI constraints and flag any options
102 * that conflict...
103 */
104
105 for (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 cptr->option->conflicted = 1;
113 }
114
115 cupsArrayDelete(active);
116
117 /*
118 * Return the number of conflicts found...
119 */
120
121 return (conflicts);
122}
123
124
125/*
126 * 'ppdInstallableConflict()' - Test whether an option choice conflicts with
127 * an installable option.
128 *
129 * This function tests whether a particular option choice is available based
130 * on constraints against options in the "InstallableOptions" group.
131 *
132 * @since CUPS 1.4@
133 */
134
135int /* O - 1 if conflicting, 0 if not conflicting */
136ppdInstallableConflict(
137 ppd_file_t *ppd, /* I - PPD file */
138 const char *option, /* I - Option */
139 const char *choice) /* I - Choice */
140{
141 cups_array_t *active; /* Active conflicts */
142 cups_option_t test; /* Test against this option */
143
144
145 /*
146 * Range check input...
147 */
148
149 if (!ppd || !option || !choice)
150 return (0);
151
152 /*
153 * Test constraints using the new option...
154 */
155
156 test.name = (char *)option;
157 test.value = (char *)choice;
158 active = ppd_test_constraints(ppd, 1, &test,
159 _PPD_INSTALLABLE_CONSTRAINTS);
160
161 cupsArrayDelete(active);
162
163 return (active != NULL);
164}
165
166
167/*
168 * 'cupsResolveConflicts()' - Resolve conflicts in a marked PPD.
169 *
170 * This function attempts to resolve any conflicts in a marked PPD, returning
171 * a list of option changes that are required to resolve any conflicts. On
172 * input, "num_options" and "options" contain any pending option changes that
173 * have not yet been marked, while "option" and "choice" contain the most recent
174 * selection which may or may not be in "num_options" or "options".
175 *
176 * On successful return, "num_options" and "options" are updated to contain
177 * "option" and "choice" along with any changes required to resolve conflicts
178 * specified in the PPD file. If option conflicts cannot be resolved,
179 * "num_options" and "options" are not changed.
180 *
181 * @code ppdResolveConflicts@ uses one of two sources of option constraint
182 * information. The preferred constraint information is defined by
183 * @code cupsUIConstraints@ and @code cupsUIResolver@ attributes - in this
184 * case, the PPD file provides constraint resolution actions. In this case,
185 * it should not be possible for @ppdResolveConflicts@ to fail, however it
186 * will do so if a resolver loop is detected.
187 *
188 * The backup constraint infomration is defined by the
189 * @code UIConstraints@ and @code NonUIConstraints@ attributes. These
190 * constraints are resolved algorithmically by selecting the default choice
191 * for the conflicting option. Unfortunately, this method is far more likely
192 * to fail.
193 *
194 * @since CUPS 1.4@
195 */
196
197int /* O - 1 on success, 0 on failure */
198cupsResolveConflicts(
199 ppd_file_t *ppd, /* I - PPD file */
200 const char *option, /* I - Newly selected option or @code NULL@ for none */
201 const char *choice, /* I - Newly selected choice or @code NULL@ for none */
202 int *num_options, /* IO - Number of additional selected options */
203 cups_option_t **options) /* IO - Additional selected options */
204{
205 int i, /* Looping var */
206 num_newopts; /* Number of new options */
207 cups_option_t *newopts; /* New options */
208 cups_array_t *active, /* Active constraints */
209 *pass, /* Resolvers for this pass */
210 *resolvers; /* Resolvers we have used */
211 _ppd_cups_uiconsts_t *consts; /* Current constraints */
212 _ppd_cups_uiconst_t *constptr; /* Current constraint */
213 ppd_attr_t *resolver; /* Current resolver */
214 const char *value; /* Selected option value */
215 int changed; /* Did we change anything? */
216 ppd_choice_t *marked; /* Marked choice */
217 ppd_option_t *ignored; /* Ignored option */
218
219
220 /*
221 * Range check input...
222 */
223
224 if (!ppd || !num_options || !options || (option == NULL) != (choice == NULL))
225 return (0);
226
227 /*
228 * Build a shadow option array...
229 */
230
231 num_newopts = 0;
232 newopts = NULL;
233
234 for (i = 0; i < *num_options; i ++)
235 num_newopts = cupsAddOption((*options)[i].name, (*options)[i].value,
236 num_newopts, &newopts);
237 if (option)
238 num_newopts = cupsAddOption(option, choice, num_newopts, &newopts);
239
240 /*
241 * Loop until we have no conflicts...
242 */
243
244 cupsArraySave(ppd->sorted_attrs);
245
246 resolvers = NULL;
247 pass = cupsArrayNew((cups_array_func_t)strcasecmp, NULL);
248
249 while ((active = ppd_test_constraints(ppd, num_newopts, newopts,
250 _PPD_ALL_CONSTRAINTS)) != NULL)
251 {
252 if (!resolvers)
253 resolvers = cupsArrayNew((cups_array_func_t)strcasecmp, NULL);
254
255 for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(active), changed = 0;
256 consts;
257 consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(active))
258 {
259 if (consts->resolver[0])
260 {
261 /*
262 * Look up the resolver...
263 */
264
265 if (cupsArrayFind(pass, consts->resolver))
266 continue; /* Already applied this resolver... */
267
268 if (cupsArrayFind(resolvers, consts->resolver))
269 {
270 /*
271 * Resolver loop!
272 */
273
274 DEBUG_printf(("ppdResolveConflicts: Resolver loop with %s!\n",
275 consts->resolver));
276 goto error;
277 }
278
279 if ((resolver = ppdFindAttr(ppd, "cupsUIResolver",
280 consts->resolver)) == NULL)
281 {
282 DEBUG_printf(("ppdResolveConflicts: Resolver %s not found!\n",
283 consts->resolver));
284 goto error;
285 }
286
287 if (!resolver->value)
288 {
289 DEBUG_printf(("ppdResolveConflicts: Resolver %s has no value!\n",
290 consts->resolver));
291 goto error;
292 }
293
294 /*
295 * Add the options from the resolver...
296 */
297
298 cupsArrayAdd(pass, consts->resolver);
299 cupsArrayAdd(resolvers, consts->resolver);
300
301 num_newopts = _ppdParseOptions(resolver->value, num_newopts, &newopts);
302 changed = 1;
303 }
304 else
305 {
306 /*
307 * Try resolving by choosing the default values for non-installable
308 * options...
309 */
310
311 for (i = consts->num_constraints, constptr = consts->constraints,
312 ignored = NULL;
313 i > 0;
314 i --, constptr ++)
315 {
316 if (constptr->installable ||
317 !strcasecmp(constptr->option->keyword, "PageSize") ||
318 !strcasecmp(constptr->option->keyword, "PageRegion"))
319 continue;
320
321 if (option && !strcasecmp(constptr->option->keyword, option))
322 {
323 ignored = constptr->option;
324 continue;
325 }
326
327 if ((value = cupsGetOption(constptr->option->keyword, num_newopts,
328 newopts)) == NULL)
329 {
330 marked = ppdFindMarkedChoice(ppd, constptr->option->keyword);
331 value = marked ? marked->choice : "";
332 }
333
334 if (strcasecmp(value, constptr->option->defchoice))
335 {
336 num_newopts = cupsAddOption(constptr->option->keyword,
337 constptr->option->defchoice,
338 num_newopts, &newopts);
339 changed = 1;
340 }
341 }
342
343 if (ignored && !changed)
344 {
345 /*
346 * No choice, have to back out this selection...
347 */
348
349 if ((value = cupsGetOption(ignored->keyword, num_newopts,
350 newopts)) == NULL)
351 {
352 marked = ppdFindMarkedChoice(ppd, ignored->keyword);
353 value = marked ? marked->choice : "";
354 }
355
356 if (strcasecmp(value, ignored->defchoice))
357 {
358 num_newopts = cupsAddOption(ignored->keyword, ignored->defchoice,
359 num_newopts, &newopts);
360 changed = 1;
361 }
362 }
363 }
364
365 if (!changed)
366 {
367 DEBUG_puts("ppdResolveConflicts: Unable to automatically resolve "
368 "constraint!");
369 goto error;
370 }
371 }
372
373 cupsArrayClear(pass);
374 cupsArrayDelete(active);
375 }
376
377 /*
378 * Free either the old or the new options depending on whether we had to
379 * apply any resolvers...
380 */
381
382 if (resolvers)
383 {
384 cupsFreeOptions(*num_options, *options);
385 *num_options = num_newopts;
386 *options = newopts;
387 }
388 else
389 cupsFreeOptions(num_newopts, newopts);
390
391 cupsArrayDelete(pass);
392 cupsArrayDelete(resolvers);
393
394 cupsArrayRestore(ppd->sorted_attrs);
395
396 return (1);
397
398 /*
399 * If we get here, we failed to resolve...
400 */
401
402 error:
403
404 cupsFreeOptions(num_newopts, newopts);
405
406 cupsArrayDelete(pass);
407 cupsArrayDelete(resolvers);
408
409 cupsArrayRestore(ppd->sorted_attrs);
410
411 return (0);
412}
413
414
415/*
416 * 'ppd_is_installable()' - Determine whether an option is in the
417 * InstallableOptions group.
418 */
419
420static int /* O - 1 if installable, 0 if normal */
421ppd_is_installable(
422 ppd_group_t *installable, /* I - InstallableOptions group */
423 const char *name) /* I - Option name */
424{
425 if (installable)
426 {
427 int i; /* Looping var */
428 ppd_option_t *option; /* Current option */
429
430
431 for (i = installable->num_options, option = installable->options;
432 i > 0;
433 i --, option ++)
434 if (!strcasecmp(option->keyword, name))
435 return (1);
436 }
437
438 return (0);
439}
440
441
442/*
443 * 'ppd_load_constraints()' - Load constraints from a PPD file.
444 */
445
446static void
447ppd_load_constraints(ppd_file_t *ppd) /* I - PPD file */
448{
449 int i; /* Looping var */
450 ppd_const_t *oldconst; /* Current UIConstraints data */
451 ppd_attr_t *constattr; /* Current cupsUIConstraints attribute */
452 _ppd_cups_uiconsts_t *consts; /* Current cupsUIConstraints data */
453 _ppd_cups_uiconst_t *constptr; /* Current constraint */
454 ppd_group_t *installable; /* Installable options group */
455 const char *vptr; /* Pointer into constraint value */
456 char option[PPD_MAX_NAME], /* Option name/MainKeyword */
457 choice[PPD_MAX_NAME], /* Choice/OptionKeyword */
458 *ptr; /* Pointer into option or choice */
459
460
461 /*
462 * Create an array to hold the constraint data...
463 */
464
465 ppd->cups_uiconstraints = cupsArrayNew(NULL, NULL);
466
467 /*
468 * Find the installable options group if it exists...
469 */
470
471 for (i = ppd->num_groups, installable = ppd->groups;
472 i > 0;
473 i --, installable ++)
474 if (!strcasecmp(installable->name, "InstallableOptions"))
475 break;
476
477 if (i <= 0)
478 installable = NULL;
479
480 /*
481 * See what kind of constraint data we have in the PPD...
482 */
483
484 if ((constattr = ppdFindAttr(ppd, "cupsUIConstraints", NULL)) != NULL)
485 {
486 /*
487 * Load new-style cupsUIConstraints data...
488 */
489
490 for (; constattr;
491 constattr = ppdFindNextAttr(ppd, "cupsUIConstraints", NULL))
492 {
493 if (!constattr->value)
494 {
495 DEBUG_puts("ppd_load_constraints: Bad cupsUIConstraints value!");
496 continue;
497 }
498
499 for (i = 0, vptr = strchr(constattr->value, '*');
500 vptr;
501 i ++, vptr = strchr(vptr + 1, '*'));
502
503 if (i == 0)
504 {
505 DEBUG_puts("ppd_load_constraints: Bad cupsUIConstraints value!");
506 continue;
507 }
508
509 if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL)
510 {
511 DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
512 "cupsUIConstraints!");
513 return;
514 }
515
516 if ((constptr = calloc(i, sizeof(_ppd_cups_uiconst_t))) == NULL)
517 {
518 free(consts);
519 DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
520 "cupsUIConstraints!");
521 return;
522 }
523
524 consts->num_constraints = i;
525 consts->constraints = constptr;
526
527 strlcpy(consts->resolver, constattr->spec, sizeof(consts->resolver));
528
529 for (i = 0, vptr = strchr(constattr->value, '*');
530 vptr;
531 i ++, vptr = strchr(vptr + 1, '*'), constptr ++)
532 {
533 /*
534 * Extract "*Option Choice" or just "*Option"...
535 */
536
537 for (vptr ++, ptr = option; *vptr && !isspace(*vptr & 255); vptr ++)
538 if (ptr < (option + sizeof(option) - 1))
539 *ptr++ = *vptr;
540
541 *ptr = '\0';
542
543 while (isspace(*vptr & 255))
544 vptr ++;
545
546 if (*vptr == '*')
547 {
548 vptr --;
549 choice[0] = '\0';
550 }
551 else
552 {
553 for (ptr = choice; *vptr && !isspace(*vptr & 255); vptr ++)
554 if (ptr < (choice + sizeof(choice) - 1))
555 *ptr++ = *vptr;
556
557 *ptr = '\0';
558 }
559
560 if (!strncasecmp(option, "Custom", 6) && !strcasecmp(choice, "True"))
561 {
562 _cups_strcpy(option, option + 6);
563 strcpy(choice, "Custom");
564 }
565
566 constptr->option = ppdFindOption(ppd, option);
567 constptr->choice = ppdFindChoice(constptr->option, choice);
568 constptr->installable = ppd_is_installable(installable, option);
569 consts->installable |= constptr->installable;
570
571 if (!constptr->option)
572 {
573 DEBUG_printf(("ppd_load_constraints: Unknown option %s!\n", option));
574 break;
575 }
576 }
577
578 if (!vptr)
579 cupsArrayAdd(ppd->cups_uiconstraints, consts);
580 else
581 {
582 free(consts->constraints);
583 free(consts);
584 }
585 }
586 }
587 else
588 {
589 /*
590 * Load old-style [Non]UIConstraints data...
591 */
592
593 for (i = ppd->num_consts, oldconst = ppd->consts; i > 0; i --, oldconst ++)
594 {
595 /*
596 * Weed out nearby duplicates, since the PPD spec requires that you
597 * define both "*Foo foo *Bar bar" and "*Bar bar *Foo foo"...
598 */
599
600 if (i > 1 &&
601 !strcasecmp(oldconst[0].option1, oldconst[1].option2) &&
602 !strcasecmp(oldconst[0].choice1, oldconst[1].choice2) &&
603 !strcasecmp(oldconst[0].option2, oldconst[1].option1) &&
604 !strcasecmp(oldconst[0].choice2, oldconst[1].choice1))
605 continue;
606
607 /*
608 * Allocate memory...
609 */
610
611 if ((consts = calloc(1, sizeof(_ppd_cups_uiconsts_t))) == NULL)
612 {
613 DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
614 "UIConstraints!");
615 return;
616 }
617
618 if ((constptr = calloc(2, sizeof(_ppd_cups_uiconst_t))) == NULL)
619 {
620 free(consts);
621 DEBUG_puts("ppd_load_constraints: Unable to allocate memory for "
622 "UIConstraints!");
623 return;
624 }
625
626 /*
627 * Fill in the information...
628 */
629
630 consts->num_constraints = 2;
631 consts->constraints = constptr;
632
633 if (!strncasecmp(oldconst->option1, "Custom", 6) &&
634 !strcasecmp(oldconst->choice1, "True"))
635 {
636 constptr[0].option = ppdFindOption(ppd, oldconst->option1 + 6);
637 constptr[0].choice = ppdFindChoice(constptr[0].option, "Custom");
638 constptr[0].installable = 0;
639 }
640 else
641 {
642 constptr[0].option = ppdFindOption(ppd, oldconst->option1);
643 constptr[0].choice = ppdFindChoice(constptr[0].option,
644 oldconst->choice1);
645 constptr[0].installable = ppd_is_installable(installable,
646 oldconst->option1);
647 }
648
649 if (!constptr[0].option)
650 {
651 DEBUG_printf(("ppd_load_constraints: Unknown option %s!\n",
652 oldconst->option1));
653 free(consts->constraints);
654 free(consts);
655 continue;
656 }
657
658 if (!strncasecmp(oldconst->option2, "Custom", 6) &&
659 !strcasecmp(oldconst->choice2, "True"))
660 {
661 constptr[1].option = ppdFindOption(ppd, oldconst->option2 + 6);
662 constptr[1].choice = ppdFindChoice(constptr[1].option, "Custom");
663 constptr[1].installable = 0;
664 }
665 else
666 {
667 constptr[1].option = ppdFindOption(ppd, oldconst->option2);
668 constptr[1].choice = ppdFindChoice(constptr[1].option,
669 oldconst->choice2);
670 constptr[1].installable = ppd_is_installable(installable,
671 oldconst->option2);
672 }
673
674 if (!constptr->option)
675 {
676 DEBUG_printf(("ppd_load_constraints: Unknown option %s!\n",
677 oldconst->option2));
678 free(consts->constraints);
679 free(consts);
680 continue;
681 }
682
683 consts->installable = constptr[0].installable || constptr[1].installable;
684
685 /*
686 * Add it to the constraints array...
687 */
688
689 cupsArrayAdd(ppd->cups_uiconstraints, consts);
690 }
691 }
692}
693
694
695
696/*
697 * 'ppd_test_constraints()' - See if any constraints are active.
698 */
699
700static cups_array_t * /* O - Array of active constraints */
701ppd_test_constraints(
702 ppd_file_t *ppd, /* I - PPD file */
703 int num_options, /* I - Number of additional options */
704 cups_option_t *options, /* I - Additional options */
705 int which) /* I - Which constraints to test */
706{
707 int i; /* Looping var */
708 _ppd_cups_uiconsts_t *consts; /* Current constraints */
709 _ppd_cups_uiconst_t *constptr; /* Current constraint */
710 ppd_choice_t key, /* Search key */
711 *marked; /* Marked choice */
712 cups_array_t *active = NULL; /* Active constraints */
713 const char *value; /* Current value */
714
715
716 if (!ppd->cups_uiconstraints)
717 ppd_load_constraints(ppd);
718
719 cupsArraySave(ppd->marked);
720
721 for (consts = (_ppd_cups_uiconsts_t *)cupsArrayFirst(ppd->cups_uiconstraints);
722 consts;
723 consts = (_ppd_cups_uiconsts_t *)cupsArrayNext(ppd->cups_uiconstraints))
724 {
725 if (which != _PPD_ALL_CONSTRAINTS && which != consts->installable)
726 continue;
727
728 for (i = consts->num_constraints, constptr = consts->constraints;
729 i > 0;
730 i --, constptr ++)
731 {
732 if (constptr->choice &&
733 (!strcasecmp(constptr->option->keyword, "PageSize") ||
734 !strcasecmp(constptr->option->keyword, "PageRegion")))
735 {
736 /*
737 * PageSize and PageRegion are used depending on the selected input slot
738 * and manual feed mode. Validate against the selected page size instead
739 * of an individual option...
740 */
741
742 if ((value = cupsGetOption("PageSize", num_options, options)) == NULL)
743 if ((value = cupsGetOption("PageRegion", num_options,
744 options)) == NULL)
745 if ((value = cupsGetOption("media", num_options, options)) == NULL)
746 {
747 ppd_size_t *size = ppdPageSize(ppd, NULL);
748
749 if (size)
750 value = size->name;
751 }
752
753 if (!value || strcasecmp(value, constptr->choice->choice))
754 break;
755 }
756 else if (constptr->choice)
757 {
758 if ((value = cupsGetOption(constptr->option->keyword, num_options,
759 options)) != NULL)
760 {
761 if (strcasecmp(value, constptr->choice->choice))
762 break;
763 }
764 else if (!constptr->choice->marked)
765 break;
766 }
767 else if ((value = cupsGetOption(constptr->option->keyword, num_options,
768 options)) != NULL)
769 {
770 if (!strcasecmp(value, "None") || !strcasecmp(value, "Off") ||
771 !strcasecmp(value, "False"))
772 break;
773 }
774 else
775 {
776 key.option = constptr->option;
777
778 if ((marked = (ppd_choice_t *)cupsArrayFind(ppd->marked, &key))
779 != NULL &&
780 (!strcasecmp(marked->choice, "None") ||
781 !strcasecmp(marked->choice, "Off") ||
782 !strcasecmp(marked->choice, "False")))
783 break;
784 }
785 }
786
787 if (i <= 0)
788 {
789 if (!active)
790 active = cupsArrayNew(NULL, NULL);
791
792 cupsArrayAdd(active, consts);
793 }
794 }
795
796 cupsArrayRestore(ppd->marked);
797
798 return (active);
799}
800
801
802/*
803 * End of "$Id$".
804 */