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