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