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