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